99 #if !defined (__TXLIB_H_INCLUDED) // <<<<<<<<< THE CODE IS HERE, UNFOLD IT <<<<<<<<<<<<<<<<<<<<<<<<<
100 #define __TXLIB_H_INCLUDED
131 #define _TX_VER _TX_v_FROM_CVS ($VersionInfo: , TXLib.h, 00173a, 166, 2020-05-15 02:04:56 +0400, "Ded (Ilya Dedinsky, http://txlib.ru) <mail@txlib.ru>", $)
132 #define _TX_VERSION _TX_V_FROM_CVS ($VersionInfo: , TXLib.h, 00173a, 166, 2020-05-15 02:04:56 +0400, "Ded (Ilya Dedinsky, http://txlib.ru) <mail@txlib.ru>", $)
133 #define _TX_AUTHOR _TX_A_FROM_CVS ($VersionInfo: , TXLib.h, 00173a, 166, 2020-05-15 02:04:56 +0400, "Ded (Ilya Dedinsky, http://txlib.ru) <mail@txlib.ru>", $)
136 #define _TX_v_FROM_CVS(_1,file,ver,rev,date,auth,_2) ((0x##ver##u << 16) | 0x##rev##u)
137 #define _TX_V_FROM_CVS(_1,file,ver,rev,date,auth,_2) "TXLib [Ver: " #ver ", Rev: " #rev ", Date: " #date "]"
138 #define _TX_A_FROM_CVS(_1,file,ver,rev,date,auth,_2) "Copyright (C) " auth
151 #if !defined (_TX_MODULE)
152 #define _TX_MODULE "TXLib"
161 #if defined (__GNUC__)
163 #define _GCC_VER ( __GNUC__*100 + __GNUC_MINOR__*10 + __GNUC_PATCHLEVEL__ )
165 #define __TX_COMPILER__ "GNU g++ " TX_QUOTE (__GNUC__) "." \
166 TX_QUOTE (__GNUC_MINOR__) "." \
167 TX_QUOTE (__GNUC_PATCHLEVEL__) \
168 ", std=" TX_QUOTE (__cplusplus)
170 #elif defined (__clang__) || defined (__clang_major__)
172 #define _CLANG_VER ( __clang_major__*100 + __clang_minor__*10 + __clang_patchlevel__ )
174 #define __TX_COMPILER__ "Clang " TX_QUOTE (__clang_major__) "." \
175 TX_QUOTE (__clang_minor__) "." \
176 TX_QUOTE (__clang_patchlevel__) \
177 ", std=" TX_QUOTE (__cplusplus)
178 #elif defined (_MSC_VER)
180 #define __TX_COMPILER__ "MSVS " TX_QUOTE (_MSC_VER) \
181 ", std=" TX_QUOTE (__cplusplus)
183 #elif defined (__INTEL_COMPILER)
185 #define __TX_COMPILER__ "Intel C++ " TX_QUOTE (__INTEL_COMPILER) \
186 ", std=" TX_QUOTE (__cplusplus)
189 #define __TX_COMPILER__ "Unknown C++, std=" TX_QUOTE (__cplusplus)
194 #define TX_QUOTE(sym) _TX_QUOTE (sym)
195 #define _TX_QUOTE(sym) #sym
197 #define TX_JOIN(sym1, sym2) _TX_JOIN (sym1, sym2)
198 #define _TX_JOIN(sym1, sym2) sym1 ## sym2
202 #if (__cplusplus >= 201103L) || defined (_MSC_VER) && (_MSC_VER >= 1800) // MSVC 2013
207 #if (__cplusplus >= 201103L) || defined (_MSC_VER) && (_MSC_VER >= 1900) // MSVC 2015
209 #define _TX_CPP11_MSVC15 1
218 #if !defined (NDEBUG) && defined (_DEBUG)
219 #define _TX_BUILDMODE "DEBUG"
221 #elif !defined (NDEBUG) && !defined (_DEBUG)
222 #define _TX_BUILDMODE "Debug"
224 #elif defined (NDEBUG)
225 #define _TX_BUILDMODE "Release"
234 #define __TX_FILELINE__ __FILE__ " (" TX_QUOTE (__LINE__) ")"
246 #if defined (__GNUC__) || defined (__clang__) || defined (__clang_major__)
247 #define __TX_FUNCTION__ __PRETTY_FUNCTION__
249 #elif defined (__FUNCSIG__)
250 #define __TX_FUNCTION__ __FUNCSIG__
252 #elif defined (__FUNCTION__)
253 #define __TX_FUNCTION__ __FUNCTION__
255 #elif defined (__INTEL_COMPILER) && (__INTEL_COMPILER >= 600)
256 #define __TX_FUNCTION__ __FUNCTION__
258 #elif defined (__BORLANDC__) && (__BORLANDC__ >= 0x550)
259 #define __TX_FUNCTION__ __FUNC__
261 #elif defined (__cplusplus) && (__cplusplus >= 199711L)
262 #define __TX_FUNCTION__ __func__
264 #elif defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
265 #define __TX_FUNCTION__ __func__
267 #elif defined (__PYTHON__)
268 #error No Python. No. Using parseltongue languages can lead you to Slytherin.
271 #define __TX_FUNCTION__ "(" __TX_FILELINE__ ")"
275 #if !defined (__func__) && defined (__FUNCTION__)
276 #define __func__ __FUNCTION__
289 #if !defined (__cplusplus)
293 #error ---------------------------------------------------------------------------------------
295 #error TXLib.h: Must use C++ to compile TXLib.h. Now you are using C only.
297 #error CHECK source file EXTENSION. Maybe it is ".C". It must be ".CPP".
298 #error If your file is named, for example, "Untitled.C", go to menu [File],
299 #error then [Save As] and rename it to "Untitled.CPP". Please do NOT use spaces.
300 #error ---------------------------------------------------------------------------------------
307 #if !defined (WIN32) && !defined (__WIN32__) && !defined(_WIN32) && !defined(_WIN32_WINNT) && !defined (__CYGWIN__)
311 #error ---------------------------------------------------------------------------------------
313 #error TXLib.h: Windows (MSVC/Win32 or GCC/MinGW or Cygwin) is the only supported OS, sorry.
315 #error In Linux or MacOS, you should write your own TXLib and share it with your friends, or use wine.
316 #error ---------------------------------------------------------------------------------------
323 #if defined (UNICODE) || defined (_UNICODE)
326 #warning TXLib.h: Disabling the UNICODE
329 #undef UNICODE // Burn Unicode, burn
332 #if defined (_WINDOWS_H) || defined (_INC_WINDOWS) || defined (_WINDOWS_) || defined (__WINDOWS__)
336 #error ---------------------------------------------------------------------------------------
338 #error TXLib.h: Should include "TXLib.h" BEFORE or INSTEAD of <Windows.h> in UNICODE mode.
340 #error REARRANGE your #include directives, or DISABLE the UNICODE mode by #undef UNICODE/_UNICODE.
341 #error ---------------------------------------------------------------------------------------
350 #if defined (__STRICT_ANSI__) // Try to extend strict ANSI rules
352 #undef __STRICT_ANSI__
353 #define __STRICT_ANSI__UNDEFINED
355 #if defined (_STRING_H_) || defined (_INC_STRING) || defined (_STDIO_H_) || defined (_INC_STDIO)
359 #error ---------------------------------------------------------------------------------------
361 #error TXLib.h: Should include "TXLib.h" BEFORE <string.h> or <stdio.h> in Strict ANSI mode.
363 #error REARRANGE your #include directives, or DISABLE ANSI-compliancy by #undef __STRICT_ANSI__.
364 #error ---------------------------------------------------------------------------------------
373 #if defined (__GNUC__)
375 #pragma GCC diagnostic ignored "-Wpragmas"
377 #pragma GCC diagnostic warning "-Wall"
378 #pragma GCC diagnostic warning "-Weffc++"
379 #pragma GCC diagnostic warning "-Wextra"
381 #pragma GCC diagnostic warning "-Waggressive-loop-optimizations"
382 #pragma GCC diagnostic warning "-Walloc-zero"
383 #pragma GCC diagnostic warning "-Walloca"
384 #pragma GCC diagnostic warning "-Walloca-larger-than=8192"
385 #pragma GCC diagnostic warning "-Warray-bounds"
386 #pragma GCC diagnostic warning "-Wcast-align"
387 #pragma GCC diagnostic warning "-Wcast-qual"
388 #pragma GCC diagnostic warning "-Wchar-subscripts"
389 #pragma GCC diagnostic warning "-Wconditionally-supported"
390 #pragma GCC diagnostic warning "-Wconversion"
391 #pragma GCC diagnostic warning "-Wctor-dtor-privacy"
392 #pragma GCC diagnostic warning "-Wdangling-else"
393 #pragma GCC diagnostic warning "-Wduplicated-branches"
394 #pragma GCC diagnostic warning "-Wempty-body"
395 #pragma GCC diagnostic warning "-Wfloat-equal"
396 #pragma GCC diagnostic warning "-Wformat-nonliteral"
397 #pragma GCC diagnostic warning "-Wformat-overflow=2"
398 #pragma GCC diagnostic warning "-Wformat-security"
399 #pragma GCC diagnostic warning "-Wformat-signedness"
400 #pragma GCC diagnostic warning "-Wformat-truncation=2"
401 #pragma GCC diagnostic warning "-Wformat=2"
402 #pragma GCC diagnostic warning "-Wlarger-than=8192"
403 #pragma GCC diagnostic warning "-Wlogical-op"
404 #pragma GCC diagnostic warning "-Wmissing-declarations"
405 #pragma GCC diagnostic warning "-Wnarrowing"
406 #pragma GCC diagnostic warning "-Wnon-virtual-dtor"
407 #pragma GCC diagnostic warning "-Wnonnull"
408 #pragma GCC diagnostic warning "-Wopenmp-simd"
409 #pragma GCC diagnostic warning "-Woverloaded-virtual"
410 #pragma GCC diagnostic warning "-Wpacked"
411 #pragma GCC diagnostic warning "-Wpointer-arith"
412 #pragma GCC diagnostic warning "-Wredundant-decls"
413 #pragma GCC diagnostic warning "-Wrestrict"
414 #pragma GCC diagnostic warning "-Wshadow"
415 #pragma GCC diagnostic warning "-Wsign-promo"
416 #pragma GCC diagnostic warning "-Wstack-usage=8192"
417 #pragma GCC diagnostic warning "-Wstrict-aliasing"
418 #pragma GCC diagnostic warning "-Wstrict-null-sentinel"
419 #pragma GCC diagnostic warning "-Wstrict-overflow=2"
420 #pragma GCC diagnostic warning "-Wstringop-overflow=4"
421 #pragma GCC diagnostic warning "-Wsuggest-attribute=noreturn"
422 #pragma GCC diagnostic warning "-Wsuggest-final-methods"
423 #pragma GCC diagnostic warning "-Wsuggest-final-types"
424 #pragma GCC diagnostic warning "-Wsuggest-override"
425 #pragma GCC diagnostic warning "-Wswitch-default"
426 #pragma GCC diagnostic warning "-Wswitch-enum"
427 #pragma GCC diagnostic warning "-Wsync-nand"
428 #pragma GCC diagnostic warning "-Wundef"
429 #pragma GCC diagnostic warning "-Wunused"
430 #pragma GCC diagnostic warning "-Wvarargs"
431 #pragma GCC diagnostic warning "-Wvariadic-macros"
432 #pragma GCC diagnostic warning "-Wvla-larger-than=8192"
434 #pragma GCC diagnostic error "-Wsizeof-array-argument"
436 #pragma GCC diagnostic ignored "-Winline"
437 #pragma GCC diagnostic ignored "-Wliteral-suffix"
438 #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
439 #pragma GCC diagnostic ignored "-Wnonnull-compare"
440 #pragma GCC diagnostic ignored "-Wold-style-cast"
441 #pragma GCC diagnostic ignored "-Wunreachable-code"
442 #pragma GCC diagnostic ignored "-Wunused-const-variable"
443 #pragma GCC diagnostic ignored "-Wunused-function"
445 #pragma GCC diagnostic warning "-Wpragmas"
449 #pragma GCC push_options
450 #pragma GCC diagnostic push
452 #pragma GCC diagnostic ignored "-Wpragmas"
454 #pragma GCC diagnostic ignored "-Waddress"
455 #pragma GCC diagnostic ignored "-Wclobbered"
456 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
457 #pragma GCC diagnostic ignored "-Wfloat-equal"
458 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
459 #pragma GCC diagnostic ignored "-Wlarger-than="
460 #pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
461 #pragma GCC diagnostic ignored "-Wredundant-decls"
462 #pragma GCC diagnostic ignored "-Wshadow"
463 #pragma GCC diagnostic ignored "-Wsign-conversion"
464 #pragma GCC diagnostic ignored "-Wstrict-aliasing"
465 #pragma GCC diagnostic ignored "-Wunused-label" // Just for fun in _txCanvas_OnCmdAbout()
466 #pragma GCC diagnostic ignored "-Wunused-value"
467 #pragma GCC diagnostic ignored "-Wformat-zero-length"
468 #pragma GCC diagnostic ignored "-Wpacked-not-aligned"
469 #pragma GCC optimize "no-strict-aliasing"
471 #if (__cplusplus < 201402L)
472 #pragma GCC diagnostic ignored "-Wsuggest-override"
475 #pragma GCC diagnostic warning "-Wpragmas"
477 #if defined (__CYGWIN__) && !defined (_TX_TESTING)
478 #pragma GCC system_header // This is not a fair play, but this is the only way to deal with Cygwin :(
483 #define _tx_thread __thread
484 #define _tx_decltype(value) __decltype (value)
486 #ifndef MINGW_HAS_SECURE_API
487 #define MINGW_HAS_SECURE_API 1
490 #if defined (TX_USE_SFML)
491 #define _GLIBCXX_NDEBUG
494 #ifndef _GLIBCXX_NDEBUG // TXLib enables _GLIBCXX_DEBUG by default. When using third-party libraries
495 #define _GLIBCXX_DEBUG // compiled without _GLIBCXX_DEBUG (SFML, for example), #define _GLIBCXX_NDEBUG
496 #define _GLIBCXX_DEBUG_PEDANTIC // *before* including TXLib.h.
499 #if defined (_WIN64) // removed in x86 because printf ("%lg", double) failure, this prints 0 always
500 #ifndef __USE_MINGW_ANSI_STDIO
501 #define __USE_MINGW_ANSI_STDIO 1
505 template <
typename T>
506 inline T _txNOP (T value) {
return value; }
509 extern "C" __declspec (dllimport)
unsigned __cdecl _controlfp (
unsigned control,
unsigned mask);
510 extern "C" void __cdecl _fpreset ();
514 #define __attribute__( attr )
515 #define _txNOP( value ) ( value )
521 #if defined (__clang__) || defined (__clang_major__)
523 #pragma clang diagnostic ignored "-Wunknown-pragmas"
525 #pragma clang diagnostic warning "-Wall"
526 #pragma clang diagnostic warning "-Weffc++"
527 #pragma clang diagnostic warning "-Wextra"
529 #pragma clang diagnostic warning "-Wcast-qual"
530 #pragma clang diagnostic warning "-Wchar-subscripts"
531 #pragma clang diagnostic warning "-Wconversion"
532 #pragma clang diagnostic warning "-Wctor-dtor-privacy"
533 #pragma clang diagnostic warning "-Wempty-body"
534 #pragma clang diagnostic warning "-Wfloat-equal"
535 #pragma clang diagnostic warning "-Wformat"
536 #pragma clang diagnostic warning "-Wformat-nonliteral"
537 #pragma clang diagnostic warning "-Wformat-security"
538 #pragma clang diagnostic warning "-Wmissing-declarations"
539 #pragma clang diagnostic warning "-Wnon-virtual-dtor"
540 #pragma clang diagnostic warning "-Woverloaded-virtual"
541 #pragma clang diagnostic warning "-Wpacked"
542 #pragma clang diagnostic warning "-Wpointer-arith"
543 #pragma clang diagnostic warning "-Wredundant-decls"
544 #pragma clang diagnostic warning "-Wshadow"
545 #pragma clang diagnostic warning "-Wsign-promo"
546 #pragma clang diagnostic warning "-Wstrict-aliasing"
547 #pragma clang diagnostic warning "-Wstrict-overflow"
548 #pragma clang diagnostic warning "-Wswitch-default"
549 #pragma clang diagnostic warning "-Wswitch-enum"
550 #pragma clang diagnostic warning "-Wunused"
551 #pragma clang diagnostic warning "-Wvariadic-macros"
553 #pragma clang diagnostic ignored "-Winvalid-source-encoding"
554 #pragma clang diagnostic ignored "-Wunused-const-variable"
555 #pragma clang diagnostic ignored "-Wunused-variable"
557 #pragma clang diagnostic warning "-Wunknown-pragmas"
561 #pragma clang diagnostic push
563 #pragma clang diagnostic ignored "-Wunknown-pragmas"
565 #pragma clang diagnostic ignored "-Wcast-align"
566 #pragma clang diagnostic ignored "-Wfloat-conversion"
567 #pragma clang diagnostic ignored "-Wmissing-braces"
568 #pragma clang diagnostic ignored "-Wmissing-field-initializers"
569 #pragma clang diagnostic ignored "-Wsign-compare"
570 #pragma clang diagnostic ignored "-Wsign-conversion"
571 #pragma clang diagnostic ignored "-Wstring-plus-int"
572 #pragma clang diagnostic ignored "-Wundef"
573 #pragma clang diagnostic ignored "-Wunused-function"
574 #pragma clang diagnostic ignored "-Wunused-value"
576 #pragma clang diagnostic warning "-Wunknown-pragmas"
604 #if defined (_MSC_VER)
606 #pragma warning (push, 4) // Set maximum warning level. This 'push' is to set the level only. It will NOT be popped.
608 #pragma warning (disable: 4616) // #pragma warning: warning number 'n' not a valid compiler warning
610 #pragma warning (disable: 4514) // Unreferenced inline function has been removed
611 #pragma warning (disable: 4710) // Function not inlined
612 #pragma warning (disable: 4786) // Identifier was truncated to '255' characters in the debug information
614 #pragma warning (error: 4715) // Not all control paths return a value
616 #pragma warning (default: 4616) // #pragma warning: warning number 'n' not a valid compiler warning
620 #pragma warning (push)
622 #pragma warning (disable: 4616) // #pragma warning: warning number 'n' not a valid compiler warning
624 #pragma warning (disable: 4091) // 'typedef': ignored on left of '...' when no variable is declared
625 #pragma warning (disable: 4124) // Using __fastcall with stack checking is ineffective
626 #pragma warning (disable: 4127) // Conditional expression is constant
627 #pragma warning (disable: 4200) // Nonstandard extension used: zero-sized array in struct/union
628 #pragma warning (disable: 4201) // Nonstandard extension used: nameless struct/union
629 #pragma warning (disable: 4351) // New behavior: elements of array will be default initialized
630 #pragma warning (disable: 4480) // Nonstandard extension used: specifying underlying type for enum 'type'
631 #pragma warning (disable: 4481) // Nonstandard extension used: override specifier 'override'
632 #pragma warning (disable: 4555) // Result of expression not used
633 #pragma warning (disable: 4611) // Interaction between '_setjmp' and C++ object destruction is non-portable
634 #pragma warning (disable: 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified
635 #pragma warning (disable: 6269) // Possibly incorrect order of operations: dereference ignored
636 #pragma warning (disable: 6285) // (<non-zero constant>) || (<non-zero constant>) is always a non-zero constant. Did you intend to use bitwize-and operator?
637 #pragma warning (disable: 6319) // Use of the comma-operator in a tested expression causes the left argument to be ignored when it has no side-effects
638 #pragma warning (disable: 6326) // Potential comparison of a constant with another constant
639 #pragma warning (disable: 26135) // Missing locking annotation
640 #pragma warning (disable: 26400) // Do not assign the result of an allocation or a function call with an owner<T> return value to a raw pointer, use owner<T> instead (i.11).
641 #pragma warning (disable: 26401) // Do not delete a raw pointer that is not an owner<T> (i.11).
642 #pragma warning (disable: 26403) // Reset or explicitly delete an owner<T> pointer 'name' (r.3).
643 #pragma warning (disable: 26408) // Avoid malloc() and free(), prefer the nothrow version of new with delete (r.10).
644 #pragma warning (disable: 26409) // Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11).
645 #pragma warning (disable: 26426) // Global initializer calls a non-constexpr function 'name' (i.22).
646 #pragma warning (disable: 26429) // Symbol 'name' is never tested for nullness, it can be marked as not_null (f.23).
647 #pragma warning (disable: 26430) // Symbol 'name' is not tested for nullness on all paths (f.23).
648 #pragma warning (disable: 26432) // If you define or delete any default operation in the type 'struct 'name'', define or delete them all (c.21).
649 #pragma warning (disable: 26435) // Function 'name' should specify exactly one of 'virtual', 'override', or 'final' (c.128).
650 #pragma warning (disable: 26438) // Avoid 'goto' (es.76).
651 #pragma warning (disable: 26440) // Function 'name' can be declared 'noexcept' (f.6).
652 #pragma warning (disable: 26446) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
653 #pragma warning (disable: 26447) // The function is declared 'noexcept' but calls function 'func' which may throw exceptions (f.6).
654 #pragma warning (disable: 26448) // Consider using gsl::finally if final action is intended (gsl.util).
655 #pragma warning (disable: 26451) // Arithmetic overflow: Using operator 'op' on a n-byte value and then casting the result to a m-byte value. Cast the value to the wider type before calling operator 'op' to avoid overflow (io.2).
656 #pragma warning (disable: 26455) // Default constructor may not throw. Declare it 'noexcept' (f.6).
657 #pragma warning (disable: 26460) // The reference argument 'stream' for function 'name' can be marked as const (con.3).
658 #pragma warning (disable: 26461) // The pointer argument 'name' for function 'name' can be marked as a pointer to const (con.3).
659 #pragma warning (disable: 26462) // The value pointed to by 'name' is assigned only once, mark it as a pointer to const (con.4).
660 #pragma warning (disable: 26475) // Do not use function style C-casts (es.49).
661 #pragma warning (disable: 26477) // Use 'nullptr' rather than 0 or NULL (es.47).
662 #pragma warning (disable: 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
663 #pragma warning (disable: 26482) // Only index into arrays using constant expressions (bounds.2).
664 #pragma warning (disable: 26483) // Value 'value' is outside the bounds (min, max) of variable 'name'. Only index into arrays using constant expressions that are within bounds of the array (bounds.2).
665 #pragma warning (disable: 26485) // Expression 'expr': No array to pointer decay (bounds.3).
666 #pragma warning (disable: 26486) // Don't pass a pointer that may be invalid to a function. Parameter 'n' 'name' in call to 'name' may be invalid (lifetime.3).
667 #pragma warning (disable: 26487) // Don't return a pointer 'name' that may be invalid (lifetime.4).
668 #pragma warning (disable: 26488) // Do not dereference a potentially null pointer: 'name'. 'name' was null at line 'n' (lifetime.1).
669 #pragma warning (disable: 26489) // Don't dereference a pointer that may be invalid: 'name'. 'name' may have been invalidated at line 'n' (lifetime.1).
670 #pragma warning (disable: 26490) // Don't use reinterpret_cast (type.1).
671 #pragma warning (disable: 26492) // Don't use const_cast to cast away const or volatile (type.3).
672 #pragma warning (disable: 26493) // Don't use C-style casts (type.4).
673 #pragma warning (disable: 26496) // The variable 'name' is assigned only once, mark it as const (con.4).
674 #pragma warning (disable: 26497) // The function 'name' could be marked constexpr if compile-time evaluation is desired (f.4).
675 #pragma warning (disable: 26812) // The enum type 'type' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
676 #pragma warning (disable: 26814) // The const variable 'name' can be computed at compile-time. Consider using constexpr (con.5).
677 #pragma warning (disable: 28125) // The function must be called from within a try/except block
678 #pragma warning (disable: 28159) // Consider using another function instead
680 #pragma warning (default: 4616) // #pragma warning: warning number 'n' not a valid compiler warning
682 #define _tx_thread __declspec (thread)
683 #define _tx_decltype(value) decltype (value)
685 #if !defined (_CLANG_VER)
687 #pragma setlocale ("russian") // Set source file encoding, see also _TX_CODEPAGE
689 #if !defined (NDEBUG)
690 #pragma check_stack ( on) // Turn on stack probes at runtime
691 #pragma strict_gs_check (push, on) // Detects stack buffer overruns
696 #define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES 1
702 #if defined (__INTEL_COMPILER)
704 #pragma warning (disable: 174) // Remark: expression has no effect
705 #pragma warning (disable: 304) // Remark: access control not specified ("public" by default)
706 #pragma warning (disable: 444) // Remark: destructor for base class "..." is not virtual
707 #pragma warning (disable: 522) // Remark: function "..." redeclared "inline" after being called
708 #pragma warning (disable: 981) // Remark: operands are evaluated in unspecified order
709 #pragma warning (disable: 1684) // Conversion from pointer to same-sized integral type (potential portability problem)
715 #if (defined (_GCC_VER) && (_GCC_VER < 472) || \
716 defined (_MSC_VER) && (_MSC_VER < 1600)) // Minimum requirements are now GCC 4.7.2 or MSVC 10.0 (2010)
720 #error ---------------------------------------------------------------------------------------
722 #error TXLib.h: This version will NOT work with GCC < 4.7.2 or MS Visual Studio < 2010, sorry.
724 #error Please use TXLib.h previous stable version/revision OR upgrade your compiler.
725 #error ---------------------------------------------------------------------------------------
732 #if defined (_GCC_VER) && (_GCC_VER >= 492)
733 #if defined (TX_USE_SPEAK) && !__has_include (<SAPI.h>)
737 #error ---------------------------------------------------------------------------------------
739 #error You have defined TX_USE_SPEAK, but your compiler do NOT have the library <SAPI.h>.
741 #error Please use compiler library set with SAPI.h included. SAPI is Microsoft Speech API
742 #error nesessary for txSpeak() to work.
743 #error ---------------------------------------------------------------------------------------
751 #if !defined (WINVER)
752 #define WINVER 0x0500 // Defaults to Windows 2000
753 #define WINDOWS_ENABLE_CPLUSPLUS // Allow use of type-limit macros in <basetsd.h>,
754 #endif // they are allowed by default if WINVER >= 0x0600.
756 #if !defined (_WIN32_WINNT)
757 #define _WIN32_WINNT WINVER // Defaults to the same as WINVER
760 #if !defined (_WIN32_IE)
761 #define _WIN32_IE WINVER // Defaults to the same as WINVER
764 #define stristr( str1, str2 ) Win32::StrStrIA ((str1), (str2))
765 #define stristrw( str1, str2 ) Win32::StrStrIW ((str1), (str2))
769 #define _USE_MATH_DEFINES 1 // Math.h's M_PI etc.
770 #define __STDC_FORMAT_MACROS 1 // PRIu64 and other PR... macros
771 #define __STDC_WANT_LIB_EXT1__ 1 // String and output *_s functions
773 #define _LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS // Wow, how long. Kudos, Clang
775 #define _ALLOW_RTCc_IN_STL 1 // MSVC C2338: /RTCc rejects conformant code, so it isn't supported by libc.
777 #define NOMINMAX 1 // Preventing 'min' and 'max' defines in Windows.h
780 #define _SECURE_SCL 1 // Enable checked STL iterators to throw an exception on incorrect use
781 #define _HAS_ITERATOR_DEBUGGING 1
782 #define _LIBCPP_DEBUG 1
785 #if defined (_MSC_VER) && defined (_DEBUG)
787 #define _CRTDBG_MAP_ALLOC // Enable MSVCRT debug heap
788 #define _new_dbg new (_NORMAL_BLOCK, __FILE__, __LINE__)
789 #define NEW new (_NORMAL_BLOCK, __FILE__, __LINE__)
797 #if !( defined (_MSC_VER) && (_MSC_VER < 1900) ) // MSVC 2015
798 #define _SECURE_SCL_THROWS 1
801 #if defined (_TX_CPP11)
803 #define _tx_delete = delete
804 #define _tx_default = default
805 #define _tx_override override
806 #define _tx_final final
817 namespace std {
enum nomeow_t { nomeow }; }
829 #if defined (_MSC_VER)
830 #pragma warning (push, 3) // MSVC: At level /Wall, some std headers emit warnings... O_o
832 #pragma warning (disable: 4365) // 'argument': conversion from 'long' to 'unsigned int', signed/unsigned mismatch
833 #pragma warning (disable: 4005) // 'name': macro redefinition
867 #include <windowsx.h>
868 #include <tlhelp32.h>
869 #include <shellapi.h>
871 #if defined (_GCC_VER)
880 #if defined (__CYGWIN__)
893 #if defined (_MSC_VER)
898 #include <ntstatus.h>
905 #if defined (_GCC_VER) || defined (_MSC_VER) && (_MSC_VER >= 1800) // MSVC 2013
906 #include <inttypes.h>
911 #if defined (TX_USE_SPEAK) //--------------------------------------------------------------------------------------
913 #endif //--------------------------------------------------------------------------------------
920 #if defined (_MSC_VER)
921 #pragma warning (pop) // MSVC: Restore max level
924 #if defined (__STRICT_ANSI__UNDEFINED)
925 #define __STRICT_ANSI__ // Redefine back
928 #if !defined (_TRUNCATE) || defined (__CYGWIN__) || defined (_MEMORY_S_DEFINED)
930 #define strncpy_s( dest, sizeof_dest, src, count ) ( strncpy ((dest), (src), MIN ((count), (sizeof_dest))) )
931 #define wcsncpy_s( dest, sizeof_dest, src, count ) ( wcsncpy ((dest), (src), MIN ((count), (sizeof_dest))) )
932 #define strncat_s( dest, sizeof_dest, src, count ) ( strncat ((dest), (src), MIN ((count), (sizeof_dest))) )
933 #define strerror_s( buf, sizeof_buf, code ) ( strncpy ((buf), strerror ((int)(code)), (sizeof_buf)-1) )
934 #define strtok_s( buf, delim, ctx ) ( (void)(ctx), strtok ((buf), (delim)) )
935 #define fopen_s( file, name, mode ) ( *(file) = fopen ((name), (mode)) )
936 #define _strlwr_s( str, sizeof_str ) ( _strlwr (str) )
938 #define ctime_s( buf, sizeof_buf, time ) ( strncpy ((buf), ctime (time), (sizeof_buf)-1) )
939 #define _controlfp_s( oldCtl, newCtl, mask ) ( assert (oldCtl), *(oldCtl) = _controlfp (newCtl, mask), 0 )
941 #define _snprintf_s snprintf
942 #define _vsnprintf_s( str, sz, trunc, format, arg ) _vsnprintf (str, sz, format, arg)
944 #define _wsplitpath_s( path, drive, szDrive, \
945 dir, szDir, name, szName, \
946 ext, szExt ) _wsplitpath ( path, drive, dir, fname, ext )
949 #if !( defined (_MSC_VER) || defined (__STDC_LIB_EXT1__) )
951 #define getenv_s( sz, buf, sizeof_buf, name ) ( (void)(sz), strncpy ((buf), getenv (name), (sizeof_buf)-1) )
955 #if defined (__CYGWIN__)
957 #undef __STRICT_ANSI__
959 typedef void _exception;
961 #define _O_TEXT O_TEXT
962 #define _fdopen fdopen
963 #define _flushall() fflush (NULL)
964 #define _getcwd getcwd
965 #define _getpid getpid
966 #define _stricmp strcasecmp
967 #define _strlwr strlwr
968 #define _strnicmp strncasecmp
969 #define _unlink unlink
970 #define _vsnprintf vsnprintf
971 #define _access access
972 #define _strdup strdup
980 #if defined (IN) // IN and OUT are defined in WinDef.h to support Microsoft SAL.
981 #undef IN // Remove them because these names are often confused with the
982 #endif // user's code.
988 #define tx_nodiscard __attribute__ (( warn_unused_result ))
989 #define tx_deprecated __attribute__ (( deprecated ))
990 #define tx_printfy( formatArgNum ) __attribute__ (( format (printf, (formatArgNum), (formatArgNum)+1) ))
991 #define tx_scanfy( formatArgNum ) __attribute__ (( format (scanf, (formatArgNum), (formatArgNum)+1) ))
993 #if !defined (PRId64) || \
994 defined (_GCC_VER) && (_GCC_VER == 492) && !defined (_WIN64) // Dev-CPP 5.11: TDM-GCC 4.9.2 MinGW64 with -m32
1003 #define PRId64 "I64d"
1004 #define PRIi64 "I64i"
1005 #define PRIo64 "I64o"
1006 #define PRIu64 "I64u"
1007 #define PRIx64 "I64x"
1008 #define PRIX64 "I64X"
1027 #ifdef FOR_DOXYGEN_ONLY
1028 namespace {
namespace TX { }}
1036 namespace {
namespace TX {
1404 #ifdef FOR_DOXYGEN_ONLY
1436 #ifdef FOR_DOXYGEN_ONLY
1441 #define TX_GREY TX_GRAY
1442 #define TX_DARKGREY TX_DARKGRAY
1443 #define TX_LIGHTGREY TX_LIGHTGRAY
1473 #ifdef FOR_DOXYGEN_ONLY
1474 COLORREF RGB (
int red,
int green,
int blue);
1501 #define txSetColour txSetColor
1519 COLORREF txColor (
double red,
double green,
double blue);
1560 #define txSetFillColour txSetFillColor
1578 COLORREF txFillColor (
double red,
double green,
double blue);
1754 inline
bool txPixel (
double x,
double y,
double red,
double green,
double blue, HDC dc =
txDC());
1802 bool txLine (
double x0,
double y0,
double x1,
double y1, HDC dc =
txDC());
1925 bool txArc (
double x0,
double y0,
double x1,
double y1,
double startAngle,
double totalAngle, HDC dc =
txDC());
1952 bool txPie (
double x0,
double y0,
double x1,
double y1,
double startAngle,
double totalAngle, HDC dc =
txDC());
1979 bool txChord (
double x0,
double y0,
double x1,
double y1,
double startAngle,
double totalAngle, HDC dc =
txDC());
2037 bool txTriangle (
double x1,
double y1,
double x2,
double y2,
double x3,
double y3);
2038 bool txTriangle (
double x1,
double y1,
double x2,
double y2,
double x3,
double y3)
2040 (void)x1; (void)y1; (void)x2; (void)y2; (void)x3; (void)y3;
2043 "txTriangle (double x1, double y1, double x2, double y2, double x3, double y3)\n\n"
2044 "Эта функция не реализована в библиотеке, потому что вы легко можете реализовать ее сами "
2045 "как функцию с параметрами, используя txPolygon(). См. \"Пример с функциями с параметрами ". "
"Ну или нарисовать тремя линиями. :)",
"TXLib сообщает", MB_ICONINFORMATION);
return false;
}
//{----------------------------------------------------------------------------------------------------------------
//! @cond INTERNAL
#define txRectandle Sleep (1000), txRectangle // Copy-protection for the function below
#define txCircle ;txCircle //
#define txSetColor ;txSetColor //
#define C0L0RREF COLORREF //
#define OxFFFFFF 0xFFFFFF //
#define lO 10 //
#define lOOO 1000 //
#define O //
bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...) tx_printfy (3);
//! @endcond
//}
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Drawing
//! @brief Рисует человечка.
//!
//! Это пример функции, которую Вы могли бы написать и сами.
//!
//! @param x X-координата человечка.
//! @param y Y-координата человечка.
//! @param sizeX Ширина человечка.
//! @param sizeY Высота человечка (также определяет размер головы).
//! @param color Цвет человечка.
//! @param handL Высота подъема левой руки (относительно высоты человечка).
//! @param handR Высота подъема правой руки (относительно высоты человечка).
//! @param twist Смещение @a спины (относительно ширины человечка).
//! @param head Высота @a подъема головы (относительно высоты человечка).
//! @param eyes Величина глаз (относительно размера головы).
//! @param wink Моргание глаз (0 -- оба открыты, -1 -- закрыт левый, +1 -- закрыт правый).
//! @param crazy Смещение глаз по вертикали (относительно размера головы).
//! @param smile Улыбка (относительно размера головы).
//! @param hair Длина волос (относительно размера головы).
//! @param wind Ветер, развевающий волосы (относительно размера головы).
//!
//! @see txSetFillColor(), txColors, RGB(), txLine(), txCircle()
//!
//! @usage @code
//! txCreateWindow (800, 600);
//!
//! //-----------+---+----+-----+-----+----------+-----+-----+-----+----+----+----+-----+-----+----+-----
//! // | x | y |sizeX|sizeY| color |handL|handR|twist|head|eyes|wink|crazy|smile|hair|wind
//! //-----------+---+----+-----+-----+----------+-----+-----+-----+----+----+----+-----+-----+----+-----
//! // | | | | | | | | | | | | | | |
//! txDrawMan (125, 250, 200, 200, TX_WHITE, 0, 0, 0, 0, 0.8, 0, 0, 1.0, 0, 0);
//! txDrawMan (325, 250, 100, 200, TX_YELLOW, 0, 0, 0, 0, 0.8, 0, 0, -1.0, 2, 0);
//! txDrawMan (525, 250, 200, 100, TX_ORANGE, 0, 0, 0, 0, 1.0, 0, -1, 0.3, 1, 0);
//! txDrawMan (725, 250, 100, 100, TX_LIGHTRED, 0, 0, 0, 0, 1.0, 0, 1, -0.3, 3, 0);
//!
//! txDrawMan (125, 550, 200, 200, TX_WHITE, 0.3, 0.3, 0, 0, 0.8, -1, 1, 0.5, 2, -1);
//! txDrawMan (325, 550, 100, 200, TX_YELLOW, -0.5, -0.5, 0, 0.1, 0.8, 1, 0, -0.5, 3, 5);
//! txDrawMan (525, 550, 200, 100, TX_ORANGE, -0.5, 0.3, 0.2, 0, 0.8, -1, 1, 0.0, 10, -5);
//! txDrawMan (725, 550, 100, 100, TX_LIGHTRED, 0.3, -0.5, -0.4, 0, 0.8, 1, -1, 0.0, 1, 1);
//! @endcode
//}----------------------------------------------------------------------------------------------------------------//////
//
void txDrawMan (int x, int y, int sizeX, int sizeY, COLORREF color, double handL, double handR, double twist, //
double head, double eyes, double wink, double crazy, double smile, double hair, double wind) //
{ //
const char msg[] = "\0/А я - человечек из библиотеки!\0/Меня объясняли на уроке!\0/Напиши меня сам!\0/"; //
// | | | | //
// Не копипастите! _/ \_ Все равно не получится! :) _/ \_ Человечки защищают _/ \_ этот код! :) _/ \_ Муаххаха! //
// //
static int count = GetTickCount(), L = 0; //////////////////////////////////////////////////////////////////////////
C0L0RREF lineColor = txGetColor();
C0L0RREF fillColor = txGetFillColor();
txSetColor (color);
txSetFillColor (color);
txLine (x + twist * sizeX, y - O.35 * sizeY, x, y - O.7 * sizeY);
txLine (x, y - O.7 * sizeY, x - sizeX/2, y - (O.7 + handL) * sizeY);
txLine (x, y - O.7 * sizeY, x + sizeX/2, y - (O.7 + handR) * sizeY);
txLine (x + twist * sizeX, y - O.35 * sizeY, x - sizeX/2, y);
txLine (x + twist * sizeX, y - O.35 * sizeY, x + sizeX/2, y);
txCircle (x, y - (O.85 + head) * sizeY, O.15 * sizeY);
txLine (x, y - (1 + head) * sizeY, x + wind/lO * sizeX, y - (1 + head + hair/lO) * sizeY);
txLine (x, y - (1 + head) * sizeY, x + (wind/lO - O.1) * sizeX, y - (1 + head + hair/lO) * sizeY);
txLine (x, y - (1 + head) * sizeY, x + (wind/lO + O.1) * sizeX, y - (1 + head + hair/lO) * sizeY);
txSetColor (~color & OxFFFFFF); // Inverse the color
txSetFillColor (~color & OxFFFFFF);
txLine (x, y - (O.8 + head - O.05 * smile/2) * sizeY, x - O.05 * sizeY, y - (O.8 + head + O.05 * smile/2) * sizeY),
txLine (x, y - (O.8 + head - O.05 * smile/2) * sizeY, x + O.05 * sizeY, y - (O.8 + head + O.05 * smile/2) * sizeY),
txNotifyIcon (4, (const char*)!! (L+'L')[msg], "\n%s\n", msg + ((unsigned) (((count -=- 1) ^=! 1) ^=~ ((0)^(0)) +1) % 3)["\"<"]);
// See above: Mouth operator -=-, Cat operator ^=!, Mouse operator ^=~ and Owl constant ((0)^(0)). Use it freely, meow
txCircle (x - O.05 * sizeY, y - (O.9 + head - O.02 * crazy) * sizeY, eyes * (1 + O.5*wink) * O.02 * sizeY);
txCircle (x + O.05 * sizeY, y - (O.9 + head + O.02 * crazy) * sizeY, eyes * (1 - O.5*wink) * O.02 * sizeY),
Sleep (lOOO + count%2);
txSetColor (TX_DARKGRAY);
txSetFillColor (TX_TRANSPARENT);
txCircle (x, y, 4);
txRectandle (x - sizeX/2, y - sizeY, x + sizeX/2, y);
txSetColor (lineColor);
txSetFillColor (fillColor);
}
//! @}
//}
//=================================================================================================================
//=================================================================================================================
//{ Drawing text
//! @name Работа с текстом
//=================================================================================================================
//! @{
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Drawing
//! @brief Рисует текст.
//!
//! @param x X-координата начальной точки текста.
//! @param y Y-координата начальной точки текста.
//! @param text Текстовая строка.
//! @param dc <i>Дескриптор контекста рисования (холста) для рисования. Необязателен.</i>
//!
//! @return Если операция была успешна -- true, иначе -- false.
//!
//! Цвет текста задается функцией txSetColor(), выравнивание (влево/вправо/по центру) -- txSetTextAlign().
//!
//! @see txSetColor(), txGetColor(), txSetFillColor(), txGetFillColor(), txColors, RGB(),
//! txSelectFont(), txSetTextAlign(), txGetTextExtent(), txGetTextExtentX(), txGetTextExtentY()
//!
//! @usage @code
//! txTextOut (100, 100, "Здесь могла бы быть Ваша реклама.");
//! @endcode
//}----------------------------------------------------------------------------------------------------------------
bool txTextOut (double x, double y, const char text[], HDC dc = txDC());
//{----------------------------------------------------------------------------------------------------------------
//! @cond INTERNAL
#undef txRectandle
#undef txCircle
#undef txSetColor
#undef C0L0RREF
#undef OxFFFFFF
#undef lO
#undef lOOO
#undef O
//! @endcond
//}
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Drawing
//! @brief Рисует текст, размещенный в прямоугольной области.
//!
//! @param x0 X-координата верхнего левого угла области.
//! @param y0 Y-координата верхнего левого угла области.
//! @param x1 X-координата нижнего правого угла области.
//! @param y1 Y-координата нижнего правого угла области.
//! @param text Текстовая строка.
//! @param format <i>Флаги форматирования текста. Необязательны. Если не указаны, то используется: центрирование,
//! перенос по словам и добавление многоточия, если надпись не умещается в область.</i>
//! @param dc <i>Дескриптор контекста рисования (холста) для рисования. Необязателен.</i>
//!
//! @return Если операция была успешна -- true, иначе -- false.
//!
//! Цвет текста задается функцией txSetColor(), выравнивание (влево/вправо/по центру) -- txSetTextAlign().
//!
//! @note Не выводит ничего, если координаты идут в неверном порядке (если x0 > x1 или y0 > y1).
//!
//! Флаги форматирования текста см. в MSDN (http://msdn.com), искать "DrawText Function (Windows)":
//! http://msdn.microsoft.com/en-us/library/dd162498%28VS.85%29.aspx.
//!
//! <b>Автоматический перенос
2046 "Ну или нарисовать тремя линиями. :)",
2047 "TXLib сообщает", MB_ICONINFORMATION);
2055 #define txRectandle Sleep (1000), txRectangle // Copy-protection for the function below
2056 #define txCircle ;txCircle //
2057 #define txSetColor ;txSetColor //
2058 #define C0L0RREF COLORREF //
2059 #define OxFFFFFF 0xFFFFFF //
2061 #define lOOO 1000 //
2064 bool txNotifyIcon (
unsigned flags,
const char title[],
const char format[], ...)
tx_printfy (3);
2112 void txDrawMan (
int x,
int y,
int sizeX,
int sizeY, COLORREF color,
double handL,
double handR,
double twist,
2113 double head,
double eyes,
double wink,
double crazy,
double smile,
double hair,
double wind)
2115 const char msg[] =
"\0/А я - человечек из библиотеки!\0/Меня объясняли на уроке!\0/Напиши меня сам!\0/";
2119 static int count = GetTickCount(), L = 0;
2127 txLine (x + twist * sizeX, y - O.35 * sizeY, x, y - O.7 * sizeY);
2129 txLine (x, y - O.7 * sizeY, x - sizeX/2, y - (O.7 + handL) * sizeY);
2130 txLine (x, y - O.7 * sizeY, x + sizeX/2, y - (O.7 + handR) * sizeY);
2132 txLine (x + twist * sizeX, y - O.35 * sizeY, x - sizeX/2, y);
2133 txLine (x + twist * sizeX, y - O.35 * sizeY, x + sizeX/2, y);
2135 txCircle (x, y - (O.85 + head) * sizeY, O.15 * sizeY);
2137 txLine (x, y - (1 + head) * sizeY, x + wind/lO * sizeX, y - (1 + head + hair/lO) * sizeY);
2138 txLine (x, y - (1 + head) * sizeY, x + (wind/lO - O.1) * sizeX, y - (1 + head + hair/lO) * sizeY);
2139 txLine (x, y - (1 + head) * sizeY, x + (wind/lO + O.1) * sizeX, y - (1 + head + hair/lO) * sizeY);
2144 txLine (x, y - (O.8 + head - O.05 * smile/2) * sizeY, x - O.05 * sizeY, y - (O.8 + head + O.05 * smile/2) * sizeY),
2145 txLine (x, y - (O.8 + head - O.05 * smile/2) * sizeY, x + O.05 * sizeY, y - (O.8 + head + O.05 * smile/2) * sizeY),
2146 txNotifyIcon (4, (
const char*)!! (L+
'L')[msg],
"\n%s\n", msg + ((
unsigned) (((count -=- 1) ^=! 1) ^=~ ((0)^(0)) +1) % 3)[
"\"<"]);
2150 txCircle (x - O.05 * sizeY, y - (O.9 + head - O.02 * crazy) * sizeY, eyes * (1 + O.5*wink) * O.02 * sizeY);
2151 txCircle (x + O.05 * sizeY, y - (O.9 + head + O.02 * crazy) * sizeY, eyes * (1 - O.5*wink) * O.02 * sizeY),
2152 Sleep (lOOO + count%2);
2158 txRectandle (x - sizeX/2, y - sizeY, x + sizeX/2, y);
2257 bool txDrawText (
double x0,
double y0,
double x1,
double y1,
const char text[],
2258 unsigned format = DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS, HDC dc =
txDC());
2288 int bold = FW_DONTCARE,
bool italic =
false,
bool underline =
false,
2289 bool strikeout =
false,
double angle = 0,
2710 bool txBitBlt (HDC destImage,
double xDest,
double yDest,
double width,
double height,
2711 HDC sourceImage,
double xSource = 0,
double ySource = 0,
unsigned operation = SRCCOPY);
2730 inline
bool txBitBlt (
double xDest,
double yDest, HDC sourceImage,
double xSource = 0,
double ySource = 0);
2790 HDC sourceImage,
double xSource = 0,
double ySource = 0, COLORREF transColor =
TX_BLACK);
2811 COLORREF transColor =
TX_BLACK,
double xSource = 0,
double ySource = 0);
2925 bool txAlphaBlend (HDC destImage,
double xDest,
double yDest,
double width,
double height,
2926 HDC sourceImage,
double xSource = 0,
double ySource = 0,
double alpha = 1.0);
2948 double xSource = 0,
double ySource = 0,
double alpha = 1.0);
3326 #if defined (_TX_CPP11)
3327 template <
int txFramesToAverage = 5>
3329 const int txFramesToAverage = 5;
3470 #ifdef FOR_DOXYGEN_ONLY
3471 inline Mouse& txCatchMouse (
bool shouldEat =
true);
3698 bool txPlaySound (const
char filename[] = NULL, DWORD mode = SND_ASYNC);
3820 intptr_t
txPlayVideo (
int x,
int y,
int width,
int height, const
char fileName[],
3821 double zoom = 0,
double gain = 1, HWND wnd =
txWindow());
3918 int txMessageBox (const
char text[] = "Муаххаха! :)", const
char header[] = "TXLib сообщает",
3919 unsigned flags = MB_ICONINFORMATION | MB_OKCANCEL);
4020 #ifdef FOR_DOXYGEN_ONLY
4021 bool txNotifyIcon (
unsigned flags,
const char title[],
const char format[], ...)
tx_printfy (3);
4131 #if defined (_TX_CPP11) || defined (FOR_DOXYGEN_ONLY)
4133 template <
typename T,
typename... ArgsT>
4134 int txPrintf (
const char* format, ArgsT... args);
4136 #define TX_PRINTF(...) ( _txPrintfCheck (__VA_ARGS__), txPrintf (__VA_ARGS__) )
4142 #if defined (_TX_CPP11) && !defined (FOR_DOXYGEN_ONLY)
4144 enum width_t :
int {};
4145 enum precision_t :
int {};
4147 inline width_t width (
int width) {
return (width_t) width; }
4148 inline precision_t precision (
int prec) {
return (precision_t) prec; }
4172 #if defined (_TX_CPP11) || defined (FOR_DOXYGEN_ONLY)
4174 template <
typename T,
typename... ArgsT>
4175 int txPrintf (std::ostringstream& stream,
const char* format, ArgsT... args);
4200 #if defined (_TX_CPP11) || defined (FOR_DOXYGEN_ONLY)
4202 template <
typename T,
typename... ArgsT>
4203 int txPrintf (
char buffer[],
size_t size,
const char* format, ArgsT... args);
4226 #if defined (_TX_CPP11) || defined (FOR_DOXYGEN_ONLY)
4228 template <
typename... ArgsT>
4229 std::string txFormat (
const char* format, ArgsT... args);
4308 #define sizearr( arr ) ( sizeof (get_size_of_an_array_with_unknown_or_nonconst_size_ (arr)) )
4313 template <
typename T,
size_t N> char (&get_size_of_an_array_with_unknown_or_nonconst_size_ (T (&) [N])) [N];
4317 #if defined (_TX_CPP11_MSVC15)
4318 template <
typename T,
size_t N> constexpr
size_t countof (
const T (&) [N] ) {
return N; }
4323 #define SIZEARR( arr ) ( sizeof (arr) / sizeof ((arr)[0]) )
4403 template <
typename Tx,
typename Ta,
typename Tb>
4406 template <
typename Tx,
typename Ta,
typename Tb>
4484 #define MAX( a, b ) ( ((a) > (b))? (a) : (b) )
4507 #define MIN( a, b ) ( ((a) < (b))? (a) : (b) )
4525 #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L // MSVC: C99 case
4527 #define ROUND( x ) ( (long) round (x) )
4531 #define ROUND( x ) ( (long) floor ((x) + 0.5) )
4569 const double txPI = asin (1.0) * 2;
4602 double sqr = pow (sqrt (x) * sqrt (x), sqrt (4.0));
4604 char str[1024] =
"";
4605 _snprintf_s (str,
sizeof (str),
"Возведение дало %g!" "!!" "!!" " Вы рады????", sqr);
4606 txMessageBox (str,
"Получен ОТВЕТ!" "!!", MB_ICONEXCLAMATION | MB_YESNO) != IDNO ||
4608 txMessageBox (
"Жаль...",
"А я так старалась , MB_ICONINFORMATION),
txMessageBox ("Уйду я от вас", "Злые вы...", MB_ICONSTOP),
exit (EXIT_FAILURE), 0
);
txNotifyIcon (1, NULL, "\n%s\n", "Высшая математика! \0" // А как это работает, а?
"С ума сойти... \0" //
"а КЭП подтверждает \0" // и кто это будет
"Главное - отчитаться\0" // поддерживать?..
"Невероятно, но факт \0"
"Кто бы мог подумать?\0" + GetTickCount() % 6 * 21);
return sqr; // Все же вернем значение. Мы же не звери
}
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Misc
//! @brief <i>Ну просто <b>очень, MB_ICONINFORMATION),
4609 txMessageBox (
"Уйду я от вас , "Злые вы...", MB_ICONSTOP),
exit (EXIT_FAILURE), 0
);
txNotifyIcon (1, NULL, "\n%s\n", "Высшая математика! \0" // А как это работает, а?
"С ума сойти... \0" //
"а КЭП подтверждает \0" // и кто это будет
"Главное - отчитаться\0" // поддерживать?..
"Невероятно, но факт \0"
"Кто бы мог подумать?\0" + GetTickCount() % 6 * 21);
return sqr; // Все же вернем значение. Мы же не звери
}
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Misc
//! @brief <i>Ну просто <b>очень, "Злые вы...", MB_ICONSTOP),
4610 exit (EXIT_FAILURE), 0
4613 txNotifyIcon (1, NULL,
"\n%s\n",
"Высшая математика! \0"
4615 "а КЭП подтверждает \0"
4616 "Главное - отчитаться\0"
4617 "Невероятно, но факт \0"
4618 "Кто бы мог подумать?\0" + GetTickCount() % 6 * 21);
4647 #ifdef FOR_DOXYGEN_ONLY
4648 #define _TX_DESTROY_3D
4651 #if defined (_TX_DESTROY_3D)
4653 #define z 0 // Читайте "Флатландию" Эбботта!
4681 #if defined (_MSC_VER) && !defined (_CLANG_VER)
4707 #define ZERO( type ) zero <type> ()
4743 #define tx_auto_func( func ) _tx_auto_fun1 ( __LINE__, func )
4744 #define _tx_auto_fun1( n, func ) _tx_auto_fun2 ( n, func )
4745 #define _tx_auto_fun2( n, func ) auto _tx_auto_func_##n = _tx_auto_func ([&]() { func; })
4747 #define tx_finally(...) tx_auto_func (__VA_ARGS__)
4749 template <
typename T>
4760 this_t& operator = (const this_t&) _tx_delete;
4763 template <typename T>
4816 #if !defined (NDEBUG)
4818 #define TX_ASSERT( cond ) _txNOP ( !(cond)? (TX_ERROR ("\a" "ВНЕЗАПНО: Логическая ошибка: " \
4819 "Неверно, что \"%s\"." TX_COMMA #cond), 1/(int)!!(cond)) : 1 )
4822 #define TX_ASSERT( cond ) ((void) 1)
4830 #define assert( cond ) TX_ASSERT (cond)
4861 #if !defined (NDEBUG)
4862 #define asserted || TX_ERROR ("\a" "Обнаружен нулевой или ложный результат.")
4865 #define asserted || _txNOP (0)
4869 #define verified asserted
4871 #define TX_NEEDED asserted
4905 #if !defined (NDEBUG)
4907 #define verify assert
4911 #define verify( expr ) ( expr )
4936 #if !defined (FOR_DOXYGEN_ONLY)
4937 #define TX_ERROR( ... ) _txError (__FILE__, __LINE__, __TX_FUNCTION__, 0, ##__VA_ARGS__)
4939 #define TX_ERROR( msg ) _txError (__FILE__, __LINE__, __TX_FUNCTION__, 0, msg)
4943 #define TX_THROW TX_ERROR
4964 #if !defined (NDEBUG)
4965 #define TX_DEBUG_ERROR(...) TX_ERROR (__VA_ARGS__)
4968 #define TX_DEBUG_ERROR(...) ((void) 0)
4994 #ifdef FOR_DOXYGEN_ONLY
4995 void txDump (
const void* address,
const char name[] =
"_txDump()",
bool pause =
true);
5001 #define txDump( ... ) _txDump ((const void*)(uintptr_t) __VA_ARGS__)
5003 #define txDump( address, ... ) _txDump ((const void*)(uintptr_t) (address), #address, ##__VA_ARGS__)
5006 void _txDump (
const void* address,
const char name[] =
"_txDump()",
bool pause =
true);
5038 #define txStackBackTrace() _txStackBackTrace (__FILE__, __LINE__, __TX_FUNCTION__, true);
5068 #define txTypename(value) txDemangle (typeid (value) .name()) .c_str()
5100 int txRegQuery (
const char* keyName,
const char* valueName,
void* value,
size_t szValue);
5261 template <
typename T>
inline T
txUnlock (T value);
5291 #define txGDI( command, dc ) ( ((dc) == txDC())? txUnlock ( (txLock(), (command)) ) : (command) )
5318 #ifndef FOR_DOXYGEN_ONLY
5391 #if defined (_TX_NOINIT)
5394 #define _TX_NOINIT 1
5398 #define _TX_NOINIT 0
5474 #ifdef FOR_DOXYGEN_ONLY
5483 #if !defined (TX_TRACE)
5491 #if defined (_TX_USE_DEVPARTNER)
5540 HDC src,
int xSrc,
int ySrc,
int wSrc,
int hSrc, DWORD rOp) = NULL;
5558 #if !defined (_TX_EXCEPTIONS_LIMIT)
5559 #define _TX_EXCEPTIONS_LIMIT 16
5562 #if !defined (_TX_FATAL_EXCEPTIONS_LIMIT)
5563 #define _TX_FATAL_EXCEPTIONS_LIMIT 16
5571 #ifdef FOR_DOXYGEN_ONLY
5572 #define _TX_FULL_STACKTRACE
5592 #if !defined (_TX_WAITABLE_PARENTS)
5593 #define _TX_WAITABLE_PARENTS "Winpty-agent.exe:Clion.exe, " \
5594 "Winpty-agent.exe:Clion64.exe, " \
5595 "starter.exe:eclipse.exe, " \
5596 "starter.exe:javaw.exe, " \
5597 "cmd.exe:devenv.exe, " \
5598 "VSDebugConsole.exe:devenv.exe, " \
5599 "consolepauser.exe:devcpp.exe, " \
5600 "cb_console_runner.exe:codeblocks.exe"
5624 #if !defined (_TX_ALLOW_KILL_PARENT) // DISCLAIMER: Я не призываю к убийству родителей.
5625 #define _TX_ALLOW_KILL_PARENT true // Это технический термин.
5626 #endif // г_дам юристам привет.
5693 #ifdef FOR_DOXYGEN_ONLY
5694 #define _TX_ALLOW_TRACE
5729 #ifdef FOR_DOXYGEN_ONLY
5733 #if !defined (TX_TRACE)
5734 #define TX_TRACE { if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__); }
5738 void _txTrace (
const char file[],
int line,
const char func[],
const char msg[] = NULL, ...);
5744 #ifndef FOR_DOXYGEN_ONLY
5757 static _txLoc _tx_thread Cur;
5762 typedef _txFuncEntry this_t;
5766 _txFuncEntry() : loc (_txLoc::Cur) { _txLoc::Cur.inTX++; _txLoc::Cur.prev = &loc; }
5767 void restore() { _txLoc::Cur = loc; }
5768 ~_txFuncEntry() { restore(); }
5771 _txFuncEntry (
const this_t&) _tx_delete;
5772 this_t& operator = (
const this_t&) _tx_delete;
5775 #if defined (_GCC_VER)
5777 inline void __txLocCurSet (
const char* _file,
int _line,
const char* _func)
5778 { _txLoc::Cur.file = _file; _txLoc::Cur.line = _line; _txLoc::Cur.func = _func; }
5782 #define __txLocCurSet( _file, _line, _func ) \
5783 ( _txLoc::Cur.file = (_file), _txLoc::Cur.line = (_line), _txLoc::Cur.func = (_func) )
5787 #define _txLocCurSet() __txLocCurSet (__FILE__, __LINE__, __TX_FUNCTION__)
5789 #define _txLocLvlSet(lvl) { _txLoc::Cur.trace = (lvl); }
5844 #if defined (_TX_ALLOW_TRACE)
5846 #define _txEntry(lvl) _txFuncEntry __txFuncEntry; { if (lvl) _txLocLvlSet (lvl); $; }
5848 #define $ { _txLocCurSet(); if (_txLoc::Cur.trace <= _TX_ALLOW_TRACE+0) { TX_TRACE; } }
5850 #define $$ { __txFuncEntry.restore(); }
5852 #elif defined (_DEBUG)
5854 #define _txEntry(lvl) _txFuncEntry __txFuncEntry; { $; }
5856 #define $ { _txLocCurSet(); }
5858 #define $$ { __txFuncEntry.restore(); }
5862 #define _txEntry(lvl) ;
5870 #define $0 _txEntry (0) // (Log level unchanged)
5871 #define $1 _txEntry (1) // Regular functions
5872 #define $2 _txEntry (2) // Resvd
5873 #define $3 _txEntry (3) // Init/Cleanup
5874 #define $4 _txEntry (4) // Init/Cleanup, misc functions
5875 #define $5 _txEntry (5) // Error handling, entry points
5876 #define $6 _txEntry (6) // Error handling, main part
5877 #define $7 _txEntry (7) // Error handling, misc functions
5878 #define $8 _txEntry (8) // Canvas worker thread
5879 #define $9 _txEntry (9) // Resvd
5884 #endif // FOR_DOXYGEN_ONLY
5916 extern CRITICAL_SECTION _txCanvas_LockBackBuf;
5950 explicit txAutoLock (CRITICAL_SECTION* cs,
bool mandatory =
true) :
5953 $1
if (!cs_)
return;
5955 if (mandatory) {$ EnterCriticalSection (cs_); }
5956 else {$ TryEnterCriticalSection (cs_)? 0 : (cs_ = NULL); }
5977 $1
new (
this)
txAutoLock (&_txCanvas_LockBackBuf, mandatory);
5986 $1
if (!cs_)
return;
5987 $ LeaveCriticalSection (cs_); cs_ = NULL;
5995 operator bool ()
const
5997 $1
return (cs_ != NULL);
6073 DIALOG = (int) 0x00000000,
6074 BUTTON = (
int) 0xFFFF0080,
6075 EDIT = (int) 0xFFFF0081,
6076 STATIC = (
int) 0xFFFF0082,
6077 LISTBOX = (int) 0xFFFF0083,
6078 SCROLLBAR = (
int) 0xFFFF0084,
6079 COMBOBOX = (int) 0xFFFF0085,
6080 END = (
int) 0x00000000
6187 virtual int dialogProc (HWND _wnd, UINT _msg, WPARAM _wParam, LPARAM _lParam);
6239 static intptr_t CALLBACK
DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam);
6281 #define TX_BEGIN_MESSAGE_MAP() \
6282 virtual int dialogProc (HWND _wnd, UINT _msg, WPARAM _wParam, LPARAM _lParam) _tx_override \
6284 int _result = txDialog::dialogProc (_wnd, _msg, _wParam, _lParam); (void) _result; \
6311 #define TX_HANDLE( id ) \
6337 #define TX_COMMAND_MAP \
6341 if (_msg == WM_COMMAND) switch (LOWORD (_wParam)) \
6366 #define TX_END_MESSAGE_MAP \
6404 const char*
txInputBox (
const char* text = NULL,
const char* caption = NULL,
const char* input = NULL)
tx_nodiscard;
6406 const char*
txInputBox (
const char* text,
const char* caption,
const char* input)
6413 if (!text) text =
"Введите строку:";
6415 if (!input) input =
"";
6425 #define ID_TEXT_ 101
6426 #define ID_INPUT_ 102
6446 {
txDialog::BUTTON,
"&OK", IDOK, 180, 10, 50, 15, BS_DEFPUSHBUTTON | WS_TABSTOP },
6447 {
txDialog::BUTTON,
"&Cancel", IDCANCEL, 180, 30, 50, 15, BS_PUSHBUTTON | WS_TABSTOP },
6503 static inputDlg dlg;
6509 dlg.dialogBox (layout);
6546 #ifndef AC_SRC_ALPHA
6547 #define AC_SRC_ALPHA 0x01
6550 #ifndef SMTO_ERRORONEXIT
6551 #define SMTO_ERRORONEXIT 0x0020
6554 #ifndef NT_CONSOLE_PROPS_SIG
6555 #define NT_CONSOLE_PROPS_SIG 0xA0000002
6559 #define NIIF_INFO 0x00000001
6560 #define NIIF_WARNING 0x00000002
6561 #define NIIF_ERROR 0x00000003
6565 #define NIF_STATE 0x00000008
6566 #define NIF_INFO 0x00000010
6569 #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
6570 #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 0x00000004
6573 #ifndef SYMOPT_CASE_INSENSITIVE
6574 #define SYMOPT_CASE_INSENSITIVE 0x00000001
6575 #define SYMOPT_UNDNAME 0x00000002
6576 #define SYMOPT_DEFERRED_LOADS 0x00000004
6577 #define SYMOPT_NO_CPP 0x00000008
6578 #define SYMOPT_LOAD_LINES 0x00000010
6579 #define SYMOPT_OMAP_FIND_NEAREST 0x00000020
6580 #define SYMOPT_LOAD_ANYTHING 0x00000040
6581 #define SYMOPT_IGNORE_CVREC 0x00000080
6582 #define SYMOPT_NO_UNQUALIFIED_LOADS 0x00000100
6583 #define SYMOPT_FAIL_CRITICAL_ERRORS 0x00000200
6584 #define SYMOPT_EXACT_SYMBOLS 0x00000400
6585 #define SYMOPT_ALLOW_ABSOLUTE_SYMBOLS 0x00000800
6586 #define SYMOPT_IGNORE_NT_SYMPATH 0x00001000
6587 #define SYMOPT_INCLUDE_32BIT_MODULES 0x00002000
6588 #define SYMOPT_PUBLICS_ONLY 0x00004000
6589 #define SYMOPT_NO_PUBLICS 0x00008000
6590 #define SYMOPT_AUTO_PUBLICS 0x00010000
6591 #define SYMOPT_NO_IMAGE_SEARCH 0x00020000
6592 #define SYMOPT_SECURE 0x00040000
6593 #define SYMOPT_NO_PROMPTS 0x00080000
6594 #define SYMOPT_ALLOW_ZERO_ADDRESS 0x01000000
6595 #define SYMOPT_DISABLE_SYMSRV_AUTODETECT 0x02000000
6596 #define SYMOPT_FAVOR_COMPRESSED 0x00800000
6597 #define SYMOPT_FLAT_DIRECTORY 0x00400000
6598 #define SYMOPT_IGNORE_IMAGEDIR 0x00200000
6599 #define SYMOPT_OVERWRITE 0x00100000
6600 #define SYMOPT_DEBUG 0x80000000
6605 #ifndef STATUS_POSSIBLE_DEADLOCK
6606 #define STATUS_POSSIBLE_DEADLOCK 0xC0000194
6609 #ifndef STATUS_FLOAT_MULTIPLE_FAULTS
6610 #define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4
6613 #ifndef STATUS_STACK_BUFFER_OVERRUN
6614 #define STATUS_STACK_BUFFER_OVERRUN 0xC0000409
6617 #ifndef STATUS_ASSERTION_FAILURE
6618 #define STATUS_ASSERTION_FAILURE 0xC0000420
6621 #ifndef STATUS_WX86_BREAKPOINT
6622 #define STATUS_WX86_BREAKPOINT 0x4000001F
6625 #ifndef DBG_PRINTEXCEPTION_C
6626 #define DBG_PRINTEXCEPTION_C 0x40010006 // OutputDebugStringA() call
6629 #ifndef DBG_PRINTEXCEPTION_WIDE_C
6630 #define DBG_PRINTEXCEPTION_WIDE_C 0x4001000A // OutputDebugStringW() call
6633 #ifndef DBG_THREAD_NAME
6634 #define DBG_THREAD_NAME 0x406D1388
6637 #define EXCEPTION_CPP_MSC 0xE06D7363 // '?msc'
6638 #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 0x19930520 // '?msc' version magic, see ehdata.h
6639 #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 0x19930521 // '?msc' version magic
6640 #define EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 0x19930522 // '?msc' version magic
6641 #define EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1 0x01994000 // '?msc' version magic
6643 #define EXCEPTION_CPP_GCC 0x20474343 // ' GCC'
6644 #define EXCEPTION_CPP_GCC_UNWIND 0x21474343 // '!GCC'
6645 #define EXCEPTION_CPP_GCC_FORCED 0x22474343 // '"GCC'
6647 #define EXCEPTION_CLR_FAILURE 0xE0434f4D // 'аCOM'
6649 #define EXCEPTION_CPP_BORLAND_BUILDER 0x0EEDFAE6 // Should never occur here
6650 #define EXCEPTION_CPP_BORLAND_DELPHI 0x0EEDFADE // Should never occur here
6652 #pragma pack (push, 1)
6654 struct CONSOLE_CURSOR_INFO
6660 struct CONSOLE_FONT_INFO
6666 struct CONSOLE_FONT_INFOEX
6673 WCHAR FaceName[LF_FACESIZE];
6676 struct DATABLOCK_HEADER
6682 struct NT_CONSOLE_PROPS
6684 DATABLOCK_HEADER dbh;
6686 WORD wFillAttribute;
6687 WORD wPopupFillAttribute;
6688 COORD dwScreenBufferSize;
6690 COORD dwWindowOrigin;
6692 DWORD nInputBufferSize;
6696 WCHAR FaceName[LF_FACESIZE];
6702 UINT uHistoryBufferSize;
6703 UINT uNumberOfHistoryBuffers;
6706 COLORREF ColorTable[16];
6712 #define INTERFACE IShellLinkDataList
6714 DECLARE_INTERFACE_ (IShellLinkDataList, IUnknown)
6717 STDMETHOD (QueryInterface) (THIS_ REFIID iid,
void** value) _tx_override PURE;
6718 STDMETHOD_(ULONG, AddRef) (THIS) _tx_override PURE;
6719 STDMETHOD_(ULONG, Release) (THIS) _tx_override PURE;
6722 STDMETHOD (AddDataBlock) (THIS_
void* dataBlock) PURE;
6723 STDMETHOD (CopyDataBlock) (THIS_ DWORD sig,
void** dataBlock) PURE;
6724 STDMETHOD (RemoveDataBlock) (THIS_ DWORD sig) PURE;
6725 STDMETHOD (GetFlags) (THIS_ DWORD* flags) PURE;
6726 STDMETHOD (SetFlags) (THIS_ DWORD flags) PURE;
6729 virtual ~IShellLinkDataList();
6732 const GUID IID_IShellLink = {0x000214ee, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
6733 const GUID IID_IShellLinkDataList = {0x45e2b4ae, 0xb1c3, 0x11d0, {0xb9,0x2f,0x00,0xa0,0xc9,0x03,0x12,0xe1}};
6734 const GUID IID_IPersistFile = {0x0000010b, 0x0000, 0x0000, {0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
6736 const GUID CLSID_SpVoice = {0x96749377, 0x3391, 0x11d2, {0x9e,0xe3,0x00,0xc0,0x4f,0x79,0x73,0x96}};
6737 const GUID IID_ISpVoice = {0x6c44df74, 0x72b9, 0x4992, {0xa1,0xec,0xef,0x99,0x6e,0x04,0x22,0xd4}};
6741 typedef DWORD NTSTATUS;
6742 typedef ULONG_PTR KAFFINITY;
6743 typedef LONG KPRIORITY;
6745 struct UNICODE_STRING
6748 USHORT MaximumLength;
6752 struct RTL_USER_PROCESS_PARAMETERS
6755 void* Reserved2[10];
6756 UNICODE_STRING ImagePathName;
6757 UNICODE_STRING CommandLine;
6767 RTL_USER_PROCESS_PARAMETERS* ProcessParameters;
6769 void* AtlThunkSListPtr;
6774 ULONG AtlThunkSListPtr32;
6775 void* Reserved9[45];
6776 BYTE Reserved10[96];
6777 void* PostProcessInitRoutine;
6778 BYTE Reserved11[128];
6779 void* Reserved12[1];
6783 struct PROCESS_BASIC_INFORMATION
6785 NTSTATUS ExitStatus;
6786 PEB* PebBaseAddress;
6787 KAFFINITY AffinityMask;
6788 KPRIORITY BasePriority;
6789 ULONG_PTR UniqueProcessId;
6790 ULONG_PTR InheritedFromUniqueProcessId;
6811 DWORD ThCallbackStack;
6812 DWORD ThCallbackBStore;
6815 DWORD64 KiCallUserMode;
6816 DWORD64 KeUserCallbackDispatcher;
6817 DWORD64 SystemRangeStart;
6818 DWORD64 KiUserExceptionDispatcher;
6821 DWORD64 Reserved[5];
6827 ADDRESS64 AddrReturn;
6828 ADDRESS64 AddrFrame;
6829 ADDRESS64 AddrStack;
6830 ADDRESS64 AddrBStore;
6831 PVOID FuncTableEntry;
6835 DWORD64 Reserved[3];
6839 struct WOW64_FLOATING_SAVE_AREA
6845 DWORD ErrorSelector;
6848 BYTE RegisterArea[80];
6852 #pragma pack (push, 4)
6854 struct WOW64_CONTEXT
6865 WOW64_FLOATING_SAVE_AREA FloatSave;
6886 BYTE ExtendedRegisters[512];
6895 ULONG64 Reserved[2];
6910 struct IMAGEHLP_LINE64
6919 typedef bool (__stdcall *PREAD_PROCESS_MEMORY_ROUTINE64) (HANDLE process, DWORD64 baseAddress,
void* buffer, DWORD size, DWORD* bytesRead);
6920 typedef void* (__stdcall *PFUNCTION_TABLE_ACCESS_ROUTINE64) (HANDLE process, DWORD64 baseAddress);
6921 typedef DWORD64 (__stdcall *PGET_MODULE_BASE_ROUTINE64) (HANDLE process, DWORD64 address);
6922 typedef DWORD64 (__stdcall *PTRANSLATE_ADDRESS_ROUTINE64) (HANDLE process, HANDLE thread, ADDRESS64* address);
6924 typedef void (*unexpected_handler)();
6926 #pragma pack (push, 4)
6928 struct MINIDUMP_THREAD_CALLBACK
6931 HANDLE ThreadHandle;
6933 ULONG SizeOfContext;
6938 struct MINIDUMP_THREAD_EX_CALLBACK
6941 HANDLE ThreadHandle;
6943 ULONG SizeOfContext;
6946 ULONG64 BackingStoreBase;
6947 ULONG64 BackingStoreEnd;
6950 struct MINIDUMP_MODULE_CALLBACK
6953 ULONG64 BaseOfImage;
6956 ULONG TimeDateStamp;
6957 VS_FIXEDFILEINFO VersionInfo;
6959 ULONG SizeOfCvRecord;
6961 ULONG SizeOfMiscRecord;
6964 struct MINIDUMP_INCLUDE_THREAD_CALLBACK
6969 struct MINIDUMP_INCLUDE_MODULE_CALLBACK
6971 ULONG64 BaseOfImage;
6974 struct MINIDUMP_MEMORY_INFO
6976 ULONG64 BaseAddress;
6977 ULONG64 AllocationBase;
6978 ULONG32 AllocationProtect;
6979 ULONG32 __alignment1;
6984 ULONG32 __alignment2;
6987 struct MINIDUMP_USER_STREAM
6994 struct MINIDUMP_USER_STREAM_INFORMATION
6996 ULONG UserStreamCount;
6997 MINIDUMP_USER_STREAM* UserStreamArray;
7000 struct MINIDUMP_CALLBACK_INPUT
7003 HANDLE ProcessHandle;
7008 MINIDUMP_THREAD_CALLBACK Thread;
7009 MINIDUMP_THREAD_EX_CALLBACK ThreadEx;
7010 MINIDUMP_MODULE_CALLBACK Module;
7011 MINIDUMP_INCLUDE_THREAD_CALLBACK IncludeThread;
7012 MINIDUMP_INCLUDE_MODULE_CALLBACK IncludeModule;
7016 struct MINIDUMP_CALLBACK_OUTPUT
7020 ULONG ModuleWriteFlags;
7021 ULONG ThreadWriteFlags;
7022 ULONG SecondaryFlags;
7032 unsigned CheckCancel;
7041 MINIDUMP_MEMORY_INFO VmRegion;
7048 struct MINIDUMP_EXCEPTION_INFORMATION
7051 EXCEPTION_POINTERS* ExceptionPointers;
7052 unsigned ClientPointers;
7055 typedef int (WINAPI* MINIDUMP_CALLBACK_ROUTINE) (
void* param, MINIDUMP_CALLBACK_INPUT* input, MINIDUMP_CALLBACK_OUTPUT* output);
7057 struct MINIDUMP_CALLBACK_INFORMATION
7059 MINIDUMP_CALLBACK_ROUTINE CallbackRoutine;
7060 void* CallbackParam;
7065 MiniDumpNormal = 0x00000000,
7066 MiniDumpWithDataSegs = 0x00000001,
7067 MiniDumpWithFullMemory = 0x00000002,
7068 MiniDumpWithHandleData = 0x00000004,
7069 MiniDumpFilterMemory = 0x00000008,
7070 MiniDumpScanMemory = 0x00000010,
7071 MiniDumpWithUnloadedModules = 0x00000020,
7072 MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
7073 MiniDumpFilterModulePaths = 0x00000080,
7074 MiniDumpWithProcessThreadData = 0x00000100,
7075 MiniDumpWithPrivateReadWriteMemory = 0x00000200,
7076 MiniDumpWithoutOptionalData = 0x00000400,
7077 MiniDumpWithFullMemoryInfo = 0x00000800,
7078 MiniDumpWithThreadInfo = 0x00001000,
7079 MiniDumpWithCodeSegs = 0x00002000,
7080 MiniDumpWithoutAuxiliaryState = 0x00004000,
7081 MiniDumpWithFullAuxiliaryState = 0x00008000,
7082 MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
7083 MiniDumpIgnoreInaccessibleMemory = 0x00020000,
7084 MiniDumpWithTokenInformation = 0x00040000
7089 #define FOREGROUND_BLACK ( 0 )
7090 #define FOREGROUND_CYAN ( FOREGROUND_BLUE | FOREGROUND_GREEN )
7091 #define FOREGROUND_MAGENTA ( FOREGROUND_BLUE | FOREGROUND_RED )
7092 #define FOREGROUND_DARKYELLOW ( FOREGROUND_GREEN | FOREGROUND_RED )
7093 #define FOREGROUND_LIGHTGRAY ( FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED )
7094 #define FOREGROUND_DARKGRAY ( FOREGROUND_INTENSITY )
7095 #define FOREGROUND_LIGHTBLUE ( FOREGROUND_BLUE | FOREGROUND_INTENSITY )
7096 #define FOREGROUND_LIGHTGREEN ( FOREGROUND_GREEN | FOREGROUND_INTENSITY )
7097 #define FOREGROUND_LIGHTCYAN ( FOREGROUND_CYAN | FOREGROUND_INTENSITY )
7098 #define FOREGROUND_LIGHTRED ( FOREGROUND_RED | FOREGROUND_INTENSITY )
7099 #define FOREGROUND_LIGHTMAGENTA ( FOREGROUND_MAGENTA | FOREGROUND_INTENSITY )
7100 #define FOREGROUND_YELLOW ( FOREGROUND_DARKYELLOW | FOREGROUND_INTENSITY )
7101 #define FOREGROUND_WHITE ( FOREGROUND_LIGHTGRAY | FOREGROUND_INTENSITY )
7103 #define BACKGROUND_BLACK ( 0 )
7104 #define BACKGROUND_CYAN ( BACKGROUND_BLUE | BACKGROUND_GREEN )
7105 #define BACKGROUND_MAGENTA ( BACKGROUND_BLUE | BACKGROUND_RED )
7106 #define BACKGROUND_DARKYELLOW ( BACKGROUND_GREEN | BACKGROUND_RED )
7107 #define BACKGROUND_GRAY ( BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED )
7108 #define BACKGROUND_DARKGRAY ( BACKGROUND_INTENSITY )
7109 #define BACKGROUND_LIGHTBLUE ( BACKGROUND_BLUE | BACKGROUND_INTENSITY )
7110 #define BACKGROUND_LIGHTGREEN ( BACKGROUND_GREEN | BACKGROUND_INTENSITY )
7111 #define BACKGROUND_LIGHTCYAN ( BACKGROUND_CYAN | BACKGROUND_INTENSITY )
7112 #define BACKGROUND_LIGHTRED ( BACKGROUND_RED | BACKGROUND_INTENSITY )
7113 #define BACKGROUND_LIGHTMAGENTA ( BACKGROUND_MAGENTA | BACKGROUND_INTENSITY )
7114 #define BACKGROUND_LIGHTYELLOW ( BACKGROUND_DARKYELLOW | BACKGROUND_INTENSITY )
7115 #define BACKGROUND_WHITE ( BACKGROUND_DARKGRAY | BACKGROUND_INTENSITY )
7127 #if defined (_MSC_VER)
7131 #pragma pack (push, 4) // EXCEPTION_RECORD:
7137 __int32 pForwardCompat;
7138 __int32 pCatchableTypeArray;
7141 struct CatchableTypeArray
7143 __int32 nCatchableTypes;
7144 __int32 arrayOfCatchableTypes[];
7147 struct CatchableType
7151 __int32 thisDisplacement[3];
7152 __int32 sizeOrOffset;
7153 __int32 copyFunction;
7156 #pragma pack (pop) // | pType (ptr/RVA) ---+------> +==================+
7164 #define _TX_MSC__CXX_DETECT_RETHROW( exc ) \
7167 (exc) -> ExceptionCode == EXCEPTION_CPP_MSC && \
7168 (exc) -> NumberParameters >= 3 && \
7170 ((exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 || \
7171 (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 || \
7172 (exc)-> ExceptionInformation[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3) && \
7174 (exc) -> ExceptionInformation[2] == 0 \
7187 #if defined (_GCC_VER)
7191 struct __cxa_exception
7196 ::std::type_info* exceptionType;
7197 void (*exceptionDestructor)(
void*);
7201 __cxa_exception* primaryException;
7206 std::unexpected_handler unexpectedHandler;
7207 std::terminate_handler terminateHandler;
7209 __cxa_exception* nextException;
7211 int handlerSwitchValue;
7212 const unsigned char* actionRecord;
7213 const unsigned char* languageSpecificData;
7217 _Unwind_Exception unwindHeader;
7220 struct __cxa_eh_globals
7222 __cxa_exception* caughtExceptions;
7223 unsigned int uncaughtExceptions;
7229 extern "C" ABI::__cxa_eh_globals* __cxa_get_globals();
7243 #define _TX_DLLIMPORT( lib, retval, name, params ) \
7244 retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, true)
7246 #define _TX_DLLIMPORT_OPT( lib, retval, name, params ) \
7247 retval (WINAPI* name) params = (retval (WINAPI*) params) _txDllImport (lib ".dll", #name, false)
7249 #define _TX_DLLIMPORT_CRT( lib, retval, name, params ) \
7250 retval ( * name) params = (retval ( *) params) _txDllImport (lib ".dll", #name, false)
7252 typedef void (*_tx_FARPROC)();
7254 _tx_FARPROC _txDllImport (
const char dllFileName[],
const char funcName[],
bool required =
true);
7260 _TX_DLLIMPORT (
"GDI32", HDC, CreateCompatibleDC, (HDC dc));
7261 _TX_DLLIMPORT (
"GDI32", HBITMAP, CreateCompatibleBitmap, (HDC dc,
int width,
int height));
7262 _TX_DLLIMPORT (
"GDI32", HGDIOBJ, GetStockObject, (
int object));
7263 _TX_DLLIMPORT (
"GDI32", HGDIOBJ, SelectObject, (HDC dc, HGDIOBJ
object));
7264 _TX_DLLIMPORT (
"GDI32", HGDIOBJ, GetCurrentObject, (HDC dc,
unsigned objectType));
7265 _TX_DLLIMPORT (
"GDI32",
int, GetObjectA, (HGDIOBJ obj,
int bufsize,
void* buffer));
7266 _TX_DLLIMPORT (
"GDI32", DWORD, GetObjectType, (HGDIOBJ
object));
7267 _TX_DLLIMPORT (
"GDI32",
bool, DeleteDC, (HDC dc));
7268 _TX_DLLIMPORT (
"GDI32",
bool, DeleteObject, (HGDIOBJ
object));
7269 _TX_DLLIMPORT (
"GDI32", COLORREF, SetTextColor, (HDC dc, COLORREF color));
7270 _TX_DLLIMPORT (
"GDI32", COLORREF, SetBkColor, (HDC dc, COLORREF color));
7271 _TX_DLLIMPORT (
"GDI32",
int, SetBkMode, (HDC dc,
int bkMode));
7272 _TX_DLLIMPORT (
"GDI32", HFONT, CreateFontA, (
int height,
int width,
int escapement,
int orientation,
7273 int weight, DWORD italic, DWORD underline, DWORD strikeout,
7274 DWORD charSet, DWORD outputPrec, DWORD clipPrec,
7275 DWORD quality, DWORD pitchAndFamily,
const char face[]));
7276 _TX_DLLIMPORT (
"GDI32",
int, EnumFontFamiliesExA, (HDC dc, LPLOGFONT logFont, FONTENUMPROC enumProc,
7277 LPARAM lParam, DWORD reserved));
7278 _TX_DLLIMPORT (
"GDI32", COLORREF, SetPixel, (HDC dc,
int x,
int y, COLORREF color));
7279 _TX_DLLIMPORT (
"GDI32", COLORREF, GetPixel, (HDC dc,
int x,
int y));
7280 _TX_DLLIMPORT (
"GDI32", HPEN, CreatePen, (
int penStyle,
int width, COLORREF color));
7281 _TX_DLLIMPORT (
"GDI32", HBRUSH, CreateSolidBrush, (COLORREF color));
7282 _TX_DLLIMPORT (
"GDI32",
bool, MoveToEx, (HDC dc,
int x,
int y, POINT* point));
7283 _TX_DLLIMPORT (
"GDI32",
bool, LineTo, (HDC dc,
int x,
int y));
7284 _TX_DLLIMPORT (
"GDI32",
bool, Polygon, (HDC dc,
const POINT points[],
int count));
7285 _TX_DLLIMPORT (
"GDI32",
bool, Polyline, (HDC dc,
const POINT points[],
int count));
7286 _TX_DLLIMPORT (
"GDI32",
bool, PolyBezier, (HDC dc,
const POINT points[],
int count));
7287 _TX_DLLIMPORT (
"GDI32",
bool, Rectangle, (HDC dc,
int x0,
int y0,
int x1,
int y1));
7288 _TX_DLLIMPORT (
"GDI32",
bool, RoundRect, (HDC dc,
int x0,
int y0,
int x1,
int y1,
int sizeX,
int sizeY));
7289 _TX_DLLIMPORT (
"GDI32",
bool, Ellipse, (HDC dc,
int x0,
int y0,
int x1,
int y1));
7290 _TX_DLLIMPORT (
"GDI32",
bool, Arc, (HDC dc,
int x0,
int y0,
int x1,
int y1,
7291 int xStart,
int yStart,
int xEnd,
int yEnd));
7292 _TX_DLLIMPORT (
"GDI32",
bool, Pie, (HDC dc,
int x0,
int y0,
int x1,
int y1,
7293 int xStart,
int yStart,
int xEnd,
int yEnd));
7294 _TX_DLLIMPORT (
"GDI32",
bool, Chord, (HDC dc,
int x0,
int y0,
int x1,
int y1,
7295 int xStart,
int yStart,
int xEnd,
int yEnd));
7296 _TX_DLLIMPORT (
"GDI32",
bool, TextOutA, (HDC dc,
int x,
int y,
const char string[],
int length));
7297 _TX_DLLIMPORT (
"GDI32", UINT, SetTextAlign, (HDC dc,
unsigned mode));
7298 _TX_DLLIMPORT (
"GDI32",
bool, GetTextExtentPoint32A, (HDC dc,
const char string[],
int length, SIZE* size));
7299 _TX_DLLIMPORT (
"GDI32",
bool, ExtFloodFill, (HDC dc,
int x,
int y, COLORREF color,
unsigned type));
7300 _TX_DLLIMPORT (
"GDI32",
bool, BitBlt, (HDC dest,
int xDest,
int yDest,
int width,
int height,
7301 HDC src,
int xSrc,
int ySrc, DWORD rOp));
7302 _TX_DLLIMPORT (
"GDI32",
bool, StretchBlt, (HDC dest,
int xDest,
int yDest,
int width,
int height,
7303 HDC src,
int xSrc,
int ySrc,
int wSrc,
int hSrc, DWORD rOp));
7304 _TX_DLLIMPORT (
"GDI32",
bool, PlgBlt, (HDC dest,
const POINT* parallelogram,
7305 HDC src,
int xSrc,
int ySrc,
int width,
int height,
7306 HBITMAP mask,
int xMask,
int yMask));
7307 _TX_DLLIMPORT (
"GDI32",
int, SetDIBitsToDevice, (HDC dc,
int xDest,
int yDest, DWORD width, DWORD height,
7308 int xSrc,
int ySrc,
unsigned startLine,
unsigned numLines,
7309 const void* data,
const BITMAPINFO* info,
unsigned colorUse));
7310 _TX_DLLIMPORT (
"GDI32",
int, GetDIBits, (HDC hdc, HBITMAP hbmp,
unsigned uStartScan,
unsigned cScanLines,
7311 void* lpvBits, BITMAPINFO* lpbi,
unsigned usage));
7312 _TX_DLLIMPORT (
"GDI32",
bool, PatBlt, (HDC dc,
int x0,
int y0,
int width,
int height, DWORD rOp));
7313 _TX_DLLIMPORT (
"GDI32",
int, SetROP2, (HDC dc,
int mode));
7314 _TX_DLLIMPORT (
"GDI32",
int, SetStretchBltMode, (HDC dc,
int mode));
7315 _TX_DLLIMPORT (
"GDI32", DWORD, GdiSetBatchLimit, (DWORD limit));
7316 _TX_DLLIMPORT (
"GDI32", HBITMAP, CreateDIBSection, (HDC dc,
const BITMAPINFO* bmInfo,
unsigned colorUsage,
void **vBits,
7317 HANDLE section, DWORD offset));
7319 _TX_DLLIMPORT (
"User32",
int, DrawTextA, (HDC dc,
const char text[],
int length, RECT* rect,
unsigned format));
7320 _TX_DLLIMPORT (
"User32", HANDLE, LoadImageA, (HINSTANCE inst,
const char name[],
unsigned type,
7321 int sizex,
int sizey,
unsigned mode));
7322 _TX_DLLIMPORT_OPT (
"User32",
bool, IsHungAppWindow, (HWND wnd));
7323 _TX_DLLIMPORT_OPT (
"User32", HWND, GhostWindowFromHungWindow, (HWND wnd));
7325 _TX_DLLIMPORT (
"WinMM",
bool, PlaySound, (
const char sound[], HMODULE mod, DWORD mode));
7327 _TX_DLLIMPORT_OPT (
"MSImg32",
bool, TransparentBlt, (HDC dest,
int destX,
int destY,
int destWidth,
int destHeight,
7328 HDC src,
int srcX,
int srcY,
int srcWidth,
int srcHeight,
7329 unsigned transparentColor));
7330 _TX_DLLIMPORT_OPT (
"MSImg32",
bool, AlphaBlend, (HDC dest,
int destX,
int destY,
int destWidth,
int destHeight,
7331 HDC src,
int srcX,
int srcY,
int srcWidth,
int srcHeight,
7332 BLENDFUNCTION blending));
7334 _TX_DLLIMPORT (
"Kernel32",
void, ExitProcess, (
unsigned retcode));
7335 _TX_DLLIMPORT (
"Kernel32",
bool, TerminateProcess, (HANDLE process,
unsigned retcode));
7336 _TX_DLLIMPORT_OPT (
"Kernel32",
void, FatalExit, (
int retcode));
7337 _TX_DLLIMPORT_OPT (
"Kernel32",
void, FatalAppExitA, (
unsigned action,
const char message[]));
7338 _TX_DLLIMPORT (
"Kernel32", HWND, GetConsoleWindow, (
void));
7339 _TX_DLLIMPORT_OPT (
"Kernel32",
bool, SetConsoleFont, (HANDLE con, DWORD fontIndex));
7340 _TX_DLLIMPORT_OPT (
"Kernel32", DWORD, GetNumberOfConsoleFonts, (
void));
7341 _TX_DLLIMPORT_OPT (
"Kernel32",
bool, GetCurrentConsoleFont, (HANDLE con,
bool maxWnd, CONSOLE_FONT_INFO* curFont));
7342 _TX_DLLIMPORT_OPT (
"Kernel32",
bool, GetCurrentConsoleFontEx, (HANDLE con,
bool maxWnd, CONSOLE_FONT_INFOEX* curFont));
7343 _TX_DLLIMPORT_OPT (
"Kernel32",
bool, SetCurrentConsoleFontEx, (HANDLE con,
bool maxWnd, CONSOLE_FONT_INFOEX* curFont));
7344 _TX_DLLIMPORT_OPT (
"Kernel32",
void, RtlCaptureContext, (CONTEXT* contextRecord));
7345 _TX_DLLIMPORT_OPT (
"Kernel32", USHORT, RtlCaptureStackBackTrace, (DWORD framesToSkip, DWORD framesToCapture,
void** backTrace, DWORD* hash));
7346 _TX_DLLIMPORT_OPT (
"Kernel32",
void*, AddVectoredExceptionHandler, (
unsigned long firstHandler, PVECTORED_EXCEPTION_HANDLER handler));
7347 _TX_DLLIMPORT_OPT (
"Kernel32",
unsigned, RemoveVectoredExceptionHandler,(
void* handler));
7348 _TX_DLLIMPORT_OPT (
"Kernel32",
bool, GetModuleHandleEx, (DWORD flags,
const char moduleName[], HMODULE* module));
7349 _TX_DLLIMPORT_OPT (
"Kernel32",
bool, IsWow64Process, (HANDLE process,
int* isWow64Process));
7350 _TX_DLLIMPORT_OPT (
"Kernel32",
bool, Wow64GetThreadContext, (HANDLE thread, WOW64_CONTEXT* context));
7351 _TX_DLLIMPORT_OPT (
"Kernel32",
bool, SetThreadStackGuarantee, (
unsigned long* stackSize));
7353 _TX_DLLIMPORT (
"OLE32", HRESULT, CoInitialize, (
void*));
7354 _TX_DLLIMPORT (
"OLE32", HRESULT, CoCreateInstance, (REFCLSID clsId, IUnknown*, DWORD, REFIID iId, PVOID* value));
7355 _TX_DLLIMPORT (
"OLE32",
void, CoUninitialize, (
void));
7357 _TX_DLLIMPORT (
"Shell32", HINSTANCE,ShellExecuteA, (HWND wnd,
const char operation[],
const char file[],
7358 const char parameters[],
const char directory[],
int showCmd));
7360 _TX_DLLIMPORT (
"ShlWAPI",
char*, StrStrIA, (
const char string[],
const char search[]));
7361 _TX_DLLIMPORT (
"ShlWAPI",
char*, StrStrIW, (
const wchar_t string[],
const wchar_t search[]));
7363 _TX_DLLIMPORT_OPT (
"NTDLL",
char*, wine_get_version, (
void));
7364 _TX_DLLIMPORT (
"NTDLL", NTSTATUS, NtQueryInformationProcess, (HANDLE process,
int infoClass,
7365 void* processInfo,
unsigned long szProcessInfo,
unsigned long* szReturnInfo));
7367 _TX_DLLIMPORT_CRT (
"MSVCRT",
void, exit, (
int retcode));
7368 _TX_DLLIMPORT_CRT (
"MSVCRT",
void, _cexit, (
void));
7369 _TX_DLLIMPORT_CRT (
"MSVCRT",
unsigned, _fpreset, (
void));
7370 _TX_DLLIMPORT_CRT (
"MSVCRT",
unsigned, _controlfp, (
unsigned control,
unsigned mask));
7371 _TX_DLLIMPORT_CRT (
"MSVCRT", uintptr_t,_beginthread, (
void (__cdecl* start_address) (
void*),
unsigned stack_size,
void* arglist));
7372 _TX_DLLIMPORT_CRT (
"MSVCRT", uintptr_t,_beginthreadex, (
void* security,
unsigned stack_size,
unsigned (__stdcall* start_address) (
void*),
7373 void *arglist,
unsigned init_flag,
unsigned* thread_addr));
7374 _TX_DLLIMPORT_CRT (
"MSVCRT",
char*, __unDName, (
char* outStr,
const char* mangledName,
int outStrLen,
7375 void* (*mallocFunc) (
size_t size),
void (*freeFunc) (
void *pointer),
7376 unsigned short flags));
7377 _TX_DLLIMPORT_CRT (
"MSVCRT", unexpected_handler, set_unexpected, (unexpected_handler handler));
7379 _TX_DLLIMPORT_OPT (
"OpenGL32", HDC, wglGetCurrentDC, (
void));
7380 _TX_DLLIMPORT_OPT (
"OpenGL32",
unsigned, glGetError, (
void));
7381 _TX_DLLIMPORT_OPT (
"Glu32",
const char*, gluErrorString, (
unsigned error));
7383 _TX_DLLIMPORT_OPT (
"DbgHelp*",
bool, MiniDumpWriteDump, (HANDLE process, DWORD processId, HANDLE file, MINIDUMP_TYPE dumpType,
7384 MINIDUMP_EXCEPTION_INFORMATION* exceptionParam,
7385 MINIDUMP_USER_STREAM_INFORMATION* userStreamParam,
7386 MINIDUMP_CALLBACK_INFORMATION* callbackParam));
7388 _TX_DLLIMPORT_OPT (
"DbgHelp*", DWORD, SymSetOptions, (DWORD options));
7389 _TX_DLLIMPORT_OPT (
"DbgHelp*",
bool, SymInitialize, (HANDLE process,
const char userSearchPath[],
bool invadeProcess));
7390 _TX_DLLIMPORT_OPT (
"DbgHelp*",
bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol));
7391 _TX_DLLIMPORT_OPT (
"DbgHelp*",
bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line));
7392 _TX_DLLIMPORT_OPT (
"DbgHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr));
7393 _TX_DLLIMPORT_OPT (
"DbgHelp*",
bool, SymCleanup, (HANDLE process));
7394 _TX_DLLIMPORT_OPT (
"DbgHelp*",
void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase));
7395 _TX_DLLIMPORT_OPT (
"DbgHelp*",
bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame,
void* ctxRecord,
7396 PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc,
7397 PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc,
7398 PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc,
7399 PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc));
7401 _TX_DLLIMPORT_OPT (
"MgwHelp*", DWORD, SymSetOptions, (DWORD options));
7402 _TX_DLLIMPORT_OPT (
"MgwHelp*",
bool, SymInitialize, (HANDLE process,
const char userSearchPath[],
bool invadeProcess));
7403 _TX_DLLIMPORT_OPT (
"MgwHelp*",
bool, SymFromAddr, (HANDLE process, DWORD64 addr, DWORD64* offset, SYMBOL_INFO* symbol));
7404 _TX_DLLIMPORT_OPT (
"MgwHelp*",
bool, SymGetLineFromAddr64, (HANDLE process, DWORD64 addr, DWORD* offset, IMAGEHLP_LINE64* line));
7405 _TX_DLLIMPORT_OPT (
"MgwHelp*", DWORD64, SymGetModuleBase64, (HANDLE process, DWORD64 addr));
7406 _TX_DLLIMPORT_OPT (
"MgwHelp*",
bool, SymCleanup, (HANDLE process));
7407 _TX_DLLIMPORT_OPT (
"MgwHelp*",
void*, SymFunctionTableAccess64, (HANDLE process, DWORD64 addrBase));
7408 _TX_DLLIMPORT_OPT (
"MgwHelp*",
bool, StackWalk64, (DWORD arch, HANDLE process, HANDLE thread, STACKFRAME64* frame,
void* ctxRecord,
7409 PREAD_PROCESS_MEMORY_ROUTINE64 readMemoryFunc,
7410 PFUNCTION_TABLE_ACCESS_ROUTINE64 tableAccessFunc,
7411 PGET_MODULE_BASE_ROUTINE64 getModuleBaseFunc,
7412 PTRANSLATE_ADDRESS_ROUTINE64 translateAddressFunc));
7430 const int _TX_IDM_ABOUT = 40000,
7431 _TX_IDM_CONSOLE = 40001,
7432 _TX_WM_CREATEWND = 0x7FF0,
7433 _TX_WM_DESTROYWND = 0x7FF1;
7437 int _txInitialize();
7440 HWND _txCanvas_CreateWindow (
const SIZE* size);
7443 bool _txCanvas_OnCREATE (HWND wnd);
7444 bool _txCanvas_OnDESTROY (HWND wnd);
7445 bool _txCanvas_OnCLOSE (HWND);
7446 bool _txCanvas_OnPAINT (HWND wnd);
7447 bool _txCanvas_OnKEYDOWN (HWND wnd, WPARAM vk, LPARAM info);
7448 bool _txCanvas_OnCHAR (HWND wnd, WPARAM ch, LPARAM info);
7449 bool _txCanvas_OnTIMER (HWND wnd, WPARAM
id);
7450 bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords);
7451 bool _txCanvas_OnMOUSELEAVE (HWND wnd);
7452 bool _txCanvas_OnCREATEWND (HWND wnd, WPARAM, LPARAM lpar);
7453 bool _txCanvas_OnDESTROYWND (HWND wnd, WPARAM, LPARAM lpar);
7454 bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd);
7455 bool _txCanvas_OnCmdABOUT (HWND wnd, WPARAM cmd);
7457 unsigned WINAPI _txCanvas_ThreadProc (
void* data);
7458 LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar);
7460 int _txCanvas_SetRefreshLock (
int count);
7462 HDC _txBuffer_Create (HWND wnd = NULL, const POINT* size = NULL, HBITMAP bitmap = NULL,
7464 bool _txBuffer_Delete (HDC* dc);
7465 bool _txBuffer_Select (HGDIOBJ obj, HDC dc =
txDC());
7467 HWND _txConsole_Attach();
7469 bool _txConsole_Detach (
bool activate);
7470 bool _txConsole_Draw (HDC dc);
7471 bool _txConsole_SetUnicodeFont();
7473 const
char* txRegisterClass (const
char classId[], WNDPROC wndProc,
unsigned style,
int backBrush,
int wndExtra);
7474 HWND txCreateExtraWindow (CREATESTRUCT createData);
7476 int _txSetFinishedText (HWND wnd);
7477 void _txPauseBeforeTermination (HWND canvas);
7478 int _txIsParentWaitable (DWORD* parentPID = NULL)
tx_nodiscard;
7481 LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar);
7484 bool _txCreateShortcut (const
char shortcutName[],
7485 const
char fileToLink[], const
char args[] = NULL, const
char workDir[] = NULL,
7486 const
char description[] = NULL,
int cmdShow = SW_SHOWNORMAL,
7487 const
char iconFile[] = NULL,
int iconIndex = 0,
int fontSize = 0,
7488 COORD bufSize =
ZERO (COORD), COORD wndSize =
ZERO (COORD), COORD wndOrg =
ZERO (COORD));
7490 void* _tx_DLGTEMPLATE_Create (
void* globalMem,
size_t bufsize, DWORD style, DWORD exStyle,
7491 WORD controls,
short x,
short y,
short cx,
short cy,
7492 const
char caption[], const
char font[], WORD fontsize,
7495 void* _tx_DLGTEMPLATE_Add (
void* dlgTemplatePtr,
size_t bufsize, DWORD style, DWORD exStyle,
7496 short x,
short y,
short cx,
short cy,
7497 WORD
id, const
char wclass[], const
char caption[]);
7499 const
char* _txError (const
char file[] = NULL,
int line = 0, const
char func[] = NULL,
unsigned color = 0,
7500 const
char msg[] = NULL, ...)
tx_printfy (5);
7501 const
char* _txProcessError (const
char file[],
int line, const
char func[],
unsigned color,
7502 const
char msg[], va_list args);
7503 void _txOnTerminate();
7504 void _txOnUnexpected();
7505 void _txOnPureCall();
7506 void _txOnNewHandlerAnsi();
7507 int _txOnNewHandler (
size_t size);
7508 void _txOnSignal (
int signal = 0,
int fpe = 0);
7509 BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type);
7510 void _txOnSecurityError (
int code,
void*);
7511 void _txOnSecurityErrorAnsi (const
char* msg,
void* ptr,
int code);
7512 int _txOnMatherr (_exception* except);
7513 void _txOnInvalidParam (const
wchar_t* expr, const
wchar_t* func, const
wchar_t* file,
7514 unsigned line, uintptr_t);
7515 int _txOnAllocHook (
int type,
void* data,
size_t size,
int use,
long request,
7516 const
unsigned char* file,
int line);
7517 int _txOnRTCFailure (
int type, const
char* file,
int line, const
char* module, const
char* format, ...)
tx_printfy (5);
7518 int _txOnErrorReport (
int type, const
char* text,
int* ret);
7519 int tx_glGetError (
int setError = INT_MIN);
7522 void _txOnExit (
int retcode);
7523 void _txOnFatalExit (
int retcode);
7524 void _txOnExitProcess (
unsigned retcode);
7525 void _txOnFatalAppExitA (
unsigned action, const
char message[]);
7526 bool _txOnTerminateProcess (HANDLE process,
unsigned retcode);
7527 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI
7528 _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter);
7529 void _txWatchdogTerminator (
void* timeout);
7531 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc);
7532 long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc);
7533 long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const
char func[]);
7534 intptr_t _txDumpExceptionSEH (
char what[], intptr_t size, const EXCEPTION_RECORD* exc, const
char func[]);
7535 intptr_t _txDumpExceptionObj (
char what[], intptr_t size,
void*
object,
size_t sizeObj, const std::type_info* type);
7536 intptr_t _txDumpExceptionCPP (
char what[], intptr_t size,
unsigned code = 0,
7537 unsigned params = 0, const ULONG_PTR info[] = NULL);
7539 void _txStackBackTrace (const
char file[] = "?",
int line = 0, const
char func[] = "?",
7540 bool readSource = true);
7541 const
char* _txCaptureStackBackTrace (
int framesToSkip = 0,
bool readSource = true,
7542 CONTEXT* context = NULL, HANDLE thread = GetCurrentThread());
7543 int _txStackWalk (
int framesToSkip,
size_t szCapture,
void* capture[],
unsigned long* backTraceHash,
7544 CONTEXT* context = NULL, HANDLE thread = GetCurrentThread());
7545 const
char* _txCaptureStackBackTraceTX (
int framesToSkip = 0,
bool readSource = false);
7547 const
char* _txSymPrintFromAddr (
void* addr = NULL, const
char format[] = NULL, ...)
tx_printfy (2);
7548 bool _txSymGetFromAddr (
void* addr, Win32::SYMBOL_INFO** symbol = NULL,
7549 Win32::IMAGEHLP_LINE64** line = NULL, const
char** module = NULL,
7550 const
char** source = NULL,
int context = 2);
7551 intptr_t _txReadSource (
char buf[], intptr_t size, const
char file[],
7552 int linStart = 0,
int linEnd = INT_MIN,
int linMark = INT_MIN);
7553 bool _txCreateMiniDump (EXCEPTION_POINTERS* exc);
7555 uintptr_t _txSetProcAddress (const
char funcName[], uintptr_t newFunc, const
char dllName[] = NULL,
7556 int useHotPatching = false, HMODULE module = NULL,
bool debug = false);
7558 PROCESSENTRY32* _txFindProcess (
unsigned pid = GetCurrentProcessId())
tx_nodiscard;
7559 bool _txKillProcess (DWORD pid);
7560 bool _txIsBadReadPtr (const
void* address);
7561 bool _txCheckSourceCP (
int needCP =
_TX_CODEPAGE,
bool verbose = true);
7562 bool _txGetCommandLine (
wchar_t cmdLine[],
size_t szCmdLine,
unsigned pid = _getpid());
7563 IMAGE_NT_HEADERS*_txGetNtHeaders (HMODULE module = GetModuleHandle (NULL))
tx_nodiscard;
7566 intptr_t _tx_snprintf_s (
char stream[], intptr_t size, const
char format[], ...)
tx_printfy (3);
7567 intptr_t _tx_vsnprintf_s (
char stream[], intptr_t size, const
char format[], va_list arg);
7568 void txReopenStdio();
7570 #if defined (_CLANG_VER) && !defined (_MSC_VER)
7571 void _txLibCppDebugFunction (std::__libcpp_debug_info
const& info);
7574 #if defined (__CYGWIN__)
7577 int _putch (
int ch);
7585 #if !defined (NDEBUG)
7587 #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \
7588 (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) && \
7589 (assert (cond), true) )
7591 #define _TX_TXWINDOW_FAILED() ( !txOK() && \
7592 (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \
7593 (TX_ERROR ("\a" "Окно рисования не создано или не в порядке."), 1) )
7595 #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \
7596 (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) && \
7597 (TX_ERROR ("Параметр \"%s\" неверен. Возможно, этот холст не создан, " \
7598 "или уже уничтожен, или не загрузилась картинка.", #dc), 1) )
7599 #define _TX_DEFAULT_HDC_FAILED(dc) ( (!(dc) && \
7600 _TX_TXWINDOW_FAILED()) || _TX_HDC_FAILED (dc) )
7603 #define _TX_ARGUMENT_FAILED( cond ) ( !(cond) && \
7604 (SetLastErrorEx (ERROR_BAD_ARGUMENTS, 0), 1) )
7606 #define _TX_TXWINDOW_FAILED() ( !txOK() && \
7607 (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) )
7609 #define _TX_HDC_FAILED( dc ) ( !Win32::GetObjectType (dc) && \
7610 (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) )
7612 #define _TX_DEFAULT_HDC_FAILED(dc) ( !(dc) && \
7613 (SetLastErrorEx (ERROR_INVALID_DATA, 0), 1) )
7620 #if !defined (NDEBUG)
7621 #define _TX_ON_DEBUG( code ) { code; }
7623 #define _TX_ON_DEBUG( code ) ;
7629 #define _TX_UNEXPECTED( ... ) $$ _txError (NULL, 0, NULL, 0, ##__VA_ARGS__)
7634 #define _TX_CALL( func, param ) ( (func)? ((func) param) : 0 )
7635 #define _TX_CALLv( func, param ) ( (func)? ((func) param) : (void)0 )
7640 #define _txWaitFor( cond, time ) { for (DWORD _t = GetTickCount() + (time); \
7641 !(cond) && GetTickCount() < _t; \
7642 Sleep (_txWindowUpdateInterval)) ; \
7644 _txTrace (__FILE__, __LINE__, NULL, "WARNING: Timeout: " #cond "."); }
7648 #define _txSetJmp() ( setjmp (_txDumpExceptionObjJmp) == 0 )
7650 #define _txClearJmp() { *(unsigned long long*) _txDumpExceptionObjJmp = 0; }
7669 int _txInitialized =
_TX_NOINIT || _txInitialize();
7671 volatile const unsigned _txCanaryFirst = 0x776F656D;
7673 volatile unsigned _txMainThreadId = 0;
7674 volatile HANDLE _txMainThread = NULL;
7676 volatile unsigned _txCanvas_ThreadId = 0;
7677 volatile HANDLE _txCanvas_Thread = NULL;
7678 volatile HWND _txCanvas_Window = NULL;
7680 HDC _txCanvas_BackBuf[2] = {NULL,
7683 RGBQUAD* _txCanvas_Pixels = NULL;
7685 HBITMAP _txStockBitmap = NULL;
7688 CRITICAL_SECTION _txCanvas_LockBackBuf = {0,-1};
7690 UINT_PTR _txCanvas_RefreshTimer = 1;
7691 volatile int _txCanvas_RefreshLock = 0;
7693 ::std::vector<HDC>* _txCanvas_UserDCs = NULL;
7695 volatile bool _txConsole_IsBlinking =
true;
7697 bool _txConsole =
false;
7698 bool _txMain =
false;
7699 bool _txIsDll =
false;
7700 volatile bool _txRunning =
false;
7701 volatile bool _txExit =
false;
7703 volatile POINT _txMousePos = {-1,-1};
7704 volatile unsigned _txMouseButtons = 0;
7706 volatile WNDPROC _txAltWndProc = NULL;
7708 _tx_thread _txLoc _txLoc::Cur = {};
7710 volatile int _txErrors = 0;
7711 int _txOGLError = 0;
7712 volatile long _txSENumber = 0;
7713 volatile long _txSEFatalNumber = 0;
7717 LPTOP_LEVEL_EXCEPTION_FILTER _txPrevUEFilter = NULL;
7719 jmp_buf _txDumpExceptionObjJmp = {};
7721 const volatile uintptr_t _txForceImport[] = { (uintptr_t) ::TerminateProcess, (uintptr_t) ::ExitProcess,
7722 (uintptr_t) ::FatalExit, (uintptr_t) ::FatalAppExitA,
7723 (uintptr_t) ::exit, (uintptr_t) Win32::_controlfp,
7724 (uintptr_t) Win32::Polyline, (uintptr_t) Win32::PolyBezier,
7725 (uintptr_t) Win32::RoundRect, (uintptr_t) Win32::RemoveVectoredExceptionHandler,
7726 (uintptr_t) Win32::PlgBlt, (uintptr_t) Win32::RtlCaptureStackBackTrace,
7727 (uintptr_t) Win32::SymInitialize, (uintptr_t) Win32::MinGW::SymInitialize,
7728 (uintptr_t) Win32::SymSetOptions, (uintptr_t) Win32::MinGW::SymSetOptions,
7729 (uintptr_t) Win32::SymGetLineFromAddr64, (uintptr_t) Win32::MinGW::SymGetLineFromAddr64,
7730 (uintptr_t) Win32::SymFromAddr, (uintptr_t) Win32::MinGW::SymFromAddr,
7731 (uintptr_t) Win32::SymCleanup, (uintptr_t) Win32::MinGW::SymCleanup,
7732 (uintptr_t) Win32::SymGetModuleBase64, (uintptr_t) Win32::MinGW::SymGetModuleBase64,
7733 (uintptr_t) Win32::SymFunctionTableAccess64, (uintptr_t) Win32::MinGW::SymFunctionTableAccess64,
7734 (uintptr_t) Win32::StackWalk64, (uintptr_t) Win32::MinGW::StackWalk64,
7735 (uintptr_t) Win32::StrStrIA, (uintptr_t) Win32::Wow64GetThreadContext };
7737 volatile const unsigned _txCanaryLast = 0x5E2E2E5E;
7755 if (_txInitialized)
return 1;
7758 #if defined (_TX_ALLOC_BREAK) && defined (_MSC_VER) // See http://msdn.microsoft.com/en-us/library/w2fhc9a3%28v=vs.90%29.aspx
7759 _CrtSetBreakAlloc (_TX_ALLOC_BREAK);
7762 #if defined (_TX_ALLOW_TRACE)
7766 _TX_ON_DEBUG (OutputDebugString (
"\n");
7769 OutputDebugString (
"\n"));
7771 _txMainThreadId = GetCurrentThreadId();
7772 _txMainThread = OpenThread (THREAD_ALL_ACCESS,
false, _txMainThreadId);
7774 $3 _txIsDll = _txInDll();
7778 $ _txConsole = ! FindAtom (
"_txConsole");
7779 $ (void) AddAtom (
"_txConsole");
7787 $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize));
7796 $ _TX_CALL (Win32::AddVectoredExceptionHandler, (1, (PVECTORED_EXCEPTION_HANDLER) _txVectoredExceptionHandler));
7797 $ _txPrevUEFilter = SetUnhandledExceptionFilter ( (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter);
7800 $ ::std::set_terminate (_txOnTerminate);
7801 $ ::std::set_new_handler (_txOnNewHandlerAnsi);
7802 $ _TX_CALL (Win32::set_unexpected, (_txOnUnexpected));
7804 #if defined (_CLANG_VER) && !defined (_MSC_VER)
7805 $ ::std::__libcpp_debug_function = _txLibCppDebugFunction;
7808 $ SetConsoleCtrlHandler (_txOnConsoleCtrlEvent,
true);
7810 $ SetErrorMode (SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
7812 #if defined (_MSC_VER)
7814 $ _set_printf_count_output (1);
7816 $ _set_new_handler (_txOnNewHandler);
7817 $ _set_new_mode (1);
7819 #if !defined (_CLANG_VER)
7821 $ _CrtSetDbgFlag (_CRTDBG_ALLOC_MEM_DF | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_LEAK_CHECK_DF);
7822 $ _CrtSetAllocHook (_txOnAllocHook);
7824 $
unsigned mode = _CRTDBG_MODE_FILE;
7825 $
if (_CrtSetReportHook2 (_CRT_RPTHOOK_INSTALL, (_CRT_REPORT_HOOK) _txOnErrorReport) > 0) mode = 0;
7827 $ _CrtSetReportMode (_CRT_WARN, _CRTDBG_MODE_DEBUG | mode);
7828 $ _CrtSetReportMode (_CRT_ERROR, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW);
7829 $ _CrtSetReportMode (_CRT_ASSERT, _CRTDBG_MODE_DEBUG | mode | _CRTDBG_MODE_WNDW);
7830 $ _CrtSetReportFile (_CRT_WARN, _CRTDBG_FILE_STDERR);
7831 $ _CrtSetReportFile (_CRT_ERROR, _CRTDBG_FILE_STDERR);
7832 $ _CrtSetReportFile (_CRT_ASSERT, _CRTDBG_FILE_STDERR);
7836 $ _set_abort_behavior (_WRITE_ABORT_MSG, _WRITE_ABORT_MSG);
7837 $ _set_abort_behavior (0, _CALL_REPORTFAULT);
7839 $ _RTC_SetErrorFunc (_txOnRTCFailure);
7840 $ _set_purecall_handler (_txOnPureCall);
7841 $ _set_invalid_parameter_handler (_txOnInvalidParam);
7845 #if defined (__STDC_LIB_EXT1__)
7846 $ ::std::set_constraint_handler_s (_txOnSecurityErrorAnsi);
7849 #if !defined (__CYGWIN__) && defined (_GCC_VER) && (_GCC_VER >= 530) && !defined (i386)
7850 $ __setusermatherr (_txOnMatherr);
7853 #if !defined (__CYGWIN__)
7854 $ _set_error_mode (_OUT_TO_MSGBOX | _OUT_TO_STDERR);
7857 $ HWND console = _txConsole_Attach();
7861 $ InitializeCriticalSection (&_txCanvas_LockBackBuf);
7863 $ _txSetProcAddress (
"ExitProcess", (uintptr_t) _txOnExitProcess,
"KERNEL32.DLL");
7864 $ _txSetProcAddress (
"TerminateProcess", (uintptr_t) _txOnTerminateProcess,
"KERNEL32.DLL");
7865 $ _txSetProcAddress (
"FatalExit", (uintptr_t) _txOnFatalExit,
"KERNEL32.DLL");
7866 $ _txSetProcAddress (
"FatalAppExitA", (uintptr_t) _txOnFatalAppExitA,
"KERNEL32.DLL");
7867 $ _txSetProcAddress (
"UnhandledExceptionFilter", (uintptr_t) _txUnhandledExceptionFilter,
"KERNEL32.DLL",
true);
7868 $ _txSetProcAddress (
"SetUnhandledExceptionFilter", (uintptr_t) _txOnSetUnhandledExceptionFilter,
"KERNEL32.DLL");
7869 $ _txSetProcAddress (
"exit", (uintptr_t) _txOnExit);
7870 $ _txSetProcAddress (
"_cexit", (uintptr_t) _txOnCExit);
7872 $ atexit (_txCleanup);
7880 $ srand ((
unsigned) time (NULL));
7885 #if !defined (__CYGWIN__)
7890 $ HDC dc = Win32::CreateCompatibleDC (NULL); dc
asserted;
7891 $ _txStockBitmap = (HBITMAP) Win32::SelectObject (dc, Win32::CreateCompatibleBitmap (dc, 1, 1)); _txStockBitmap
asserted;
7892 $ Win32::DeleteObject (Win32::SelectObject (dc, _txStockBitmap))
asserted;
7900 bool _txCheckSourceCP (
int needCP ,
bool verbose )
7902 $3
const char* sCodePage = NULL;
7905 $
switch (((
unsigned const char*)
"А ) [0])
{
case 192: {$ codePage = 1251; sCodePage = "1251."; break; }
case 208: {$ codePage = 65001; sCodePage = "UTF-8."; break; }
case 128: {$ codePage = 866; sCodePage = "866."; break; }
case 225: {$ codePage = 20866; sCodePage = "KOI-8, waaat?!"; break; }
default: {$ codePage = -1; sCodePage = "(Unknown)"; break; }
}
$ if (codePage != needCP && verbose)
{
$ *_txTraceSE = ' '; // No stack trace please
$ _TX_UNEXPECTED ("\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n"
"This is NOT an error of TXLib itself. Please note:\n\n"
"Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your "
"IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO "
"use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. "
"Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n"
"Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n"
"You can continue, but Russian messages and symbols may appear unreadable.",
sCodePage, needCP, needCP);
}
$ return (codePage == needCP);
}
//-----------------------------------------------------------------------------------------------------------------
_tx_FARPROC _txDllImport (const char dllFileName[], const char funcName[], bool required /*= true*/)
{
if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName)) return NULL;
if (_TX_ARGUMENT_FAILED (funcName && *funcName)) return NULL;
static char dllPaths [2][MAX_PATH] = {"", ""};
if (!*dllPaths[0])
{
const char dllDir[] = "\\Windows\\";
// dllPaths[0] is relative to the TX Setup directory stored in the Registry
char* path = dllPaths[0];
txRegQuery ("HKCU\\Software\\TX Library", "ProductDir", path, MAX_PATH);
strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1);
// dllPaths[1] is relative to TXib.h file used in compilation
path = dllPaths[1];
if (strchr (__FILE__, ':'))
{
strncpy_s (path, MAX_PATH, __FILE__, sizeof (__FILE__) - 1);
}
else
{
GetCurrentDirectory (MAX_PATH, path);
strncat_s (path, MAX_PATH, "\\" __FILE__, sizeof ("\\" __FILE__) - 1);
}
if (char* dir = strrchr (path, '\\')) *dir = 0;
strncat_s (path, MAX_PATH, dllDir, sizeof (dllDir) - 1);
}
char dllName[MAX_PATH] = "", dllArch[MAX_PATH] = "";
const char* arch = (dllFileName? strchr (dllFileName, '*') : NULL);
if (arch)
{
assert (arch >= dllFileName);
strncpy_s (dllName, sizeof (dllName), dllFileName, (size_t) (arch - dllFileName));
strncat_s (dllName, sizeof (dllName), arch+1, sizeof (dllName) - 1 - strlen (dllName));
strncpy_s (dllArch, sizeof (dllArch), dllFileName, (size_t) (arch - dllFileName));
strncat_s (dllArch, sizeof (dllArch), sizeof (void*) == 8? "64" : "32", 3);
strncat_s (dllArch, sizeof (dllArch), arch+1, sizeof (dllArch) - 1 - strlen (dllArch));
}
else if (dllFileName)
{
strncat_s (dllName, sizeof (dllName), dllFileName, sizeof (dllName) - 1);
}
HMODULE dll = GetModuleHandle (dllFileName);
if (!dll) dll = GetModuleHandle (dllArch);
if (!dll) dll = GetModuleHandle (dllName);
for (int i = 0; !dll && i < (int) sizearr (dllPaths); i++)
{
char path [MAX_PATH] = "";
strncpy_s (path, sizeof (path), dllPaths[i], sizeof (dllPaths[i]));
size_t len = strlen (path);
strncpy_s (path + len, sizeof (path) - len, dllArch, sizeof (dllArch));
if (!dll) dll = LoadLibrary (path);
strncpy_s (path + len, sizeof (path) - len, dllName, sizeof (dllName));
if (!dll) dll = LoadLibrary (path);
}
if (!dll) dll = LoadLibrary (dllArch);
if (!dll) dll = LoadLibrary (dllName);
if (!dll && required) TX_ERROR ("\a" "Cannot load library \"%s%s%s\".",
dllName, (arch? "\" / \"" : ""), dllArch);
if (!dll) return NULL;
_tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName);
if (!addr && required) TX_ERROR ("\a" "Cannot import \"%s\" from library \"%s%s%s\".",
funcName, dllName, (arch? "\" / \"" : ""), dllArch);
return addr;
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013
#pragma warning (push)
#pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции
#endif
int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue)
{
if (_TX_ARGUMENT_FAILED (keyName)) return 0;
HKEY hive = NULL;
#define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \
_strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 )
if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE;
else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER;
else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT;
else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS;
else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG;
else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; }
#undef EQU_
keyName = strchr (keyName, '\\') + 1;
HKEY key = NULL;
DWORD size = 0;
bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS;
if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS;
if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS;
if (key) ok &= !!RegCloseKey (key);
return size;
}
#if defined (_MSC_VER) && (_MSC_VER == 1800)
#pragma warning (pop)
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/)
{
$1 if (!_txInitialized) _txInitialized = _txInitialize();
$ if (HWND wnd = txWindow())
{
$ SetLastErrorEx (ERROR_INVALID_DATA, 0);
$ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!"));
$ return wnd;
}
$ if (!_txIsDll)
{
$ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe
$ (void) AddAtom ("_txMain");
}
$ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; }
$ _txRunning = false;
// Store the size
$ static SIZE size = { ROUND (sizeX), ROUND (sizeY) };
$ if (centered) { size.cx *= -1; size.cy *= -1; }
// In Thread, where REAL creation lies...
$ unsigned id = 0;
$ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id);
$ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL;
$ _txWaitFor (_txRunning, 10*_TX_TIMEOUT);
$ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL;
$ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL;
$ HWND console = Win32::GetConsoleWindow();
$ DWORD proc = 0;
$ GetWindowThreadProcessId (console, &proc);
$ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable()))
{$ ShowWindow (console, _txConsoleMode); }
$ HMENU menu = GetSystemMenu (txWindow(), false);
if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); }
$ Win32::GdiSetBatchLimit (1);
$ SetLastError (0);
$ errno = 0;
#if !defined (__CYGWIN__)
$ _doserrno = 0;
#endif
$ return txWindow();
}
//-----------------------------------------------------------------------------------------------------------------
HWND txCreateExtraWindow (CREATESTRUCT createData)
{
$1 if (_TX_TXWINDOW_FAILED()) return NULL;
$ volatile HWND wnd = NULL;
$ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd;
$ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted;
$ _txWaitFor (wnd, 5*_TX_TIMEOUT);
$ return wnd;
}
//-----------------------------------------------------------------------------------------------------------------
bool txSetDefaults (HDC dc /*= txDC()*/)
{
$1 if (dc == txDC()) txUpdateWindow (false);
$ txAutoLock _lock;
$ RECT r = {};
$ GetClientRect (Win32::GetConsoleWindow(), &r) asserted;
$ SIZE szCon = { r.right - r.left, r.bottom - r.top };
$ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
$ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}};
$ GetConsoleScreenBufferInfo (out, &con);
$ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1),
(short) (con.srWindow.Bottom - con.srWindow.Top + 1) };
//{ Set defaults for graphics layer
$ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted;
$ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted;
$ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx,
0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont),
dc) asserted;
$ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted;
$ Win32::SetBkMode (dc, TRANSPARENT) asserted;
$ Win32::SetROP2 (dc, R2_COPYPEN) asserted;
$ Win32::SetStretchBltMode (dc, HALFTONE) asserted;
//}
$ if (dc != txDC())
{$ return true; }
//{ Set defaults for console layer
$ POINT szCanvas = txGetExtent (dc);
$ HGDIOBJ font = txFontExist (_txConsoleFont)?
Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx,
0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont)
:
Win32::GetStockObject (SYSTEM_FIXED_FONT);
$ _txBuffer_Select (font, _txCanvas_BackBuf[1]);
//}
//{ Scroll the console for text to go above top of window and don't mix with graphics
$ if (con.dwCursorPosition.X) _putch ('\n');
$ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top);
$ con.srWindow.Top = (short) (con.srWindow.Top + delta);
$ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta);
$ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) };
$ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY };
$ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up
$ con.dwCursorPosition.X = 0;
$ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta);
$ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window"
SetConsoleWindowInfo (out, true, &con.srWindow))
||
(ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer
SetConsoleCursorPosition (out, con.dwCursorPosition));
//}
$ txUpdateWindow (true);
return true;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txOK()
{
return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you?
_txCanaryLast == 0x5E2E2E5E &&
_txCanvas_OK()
#if defined (_MSC_VER)
&& _CrtCheckMemory()
#endif
);
}
//-----------------------------------------------------------------------------------------------------------------
//{ Cleanup
//-----------------------------------------------------------------------------------------------------------------
// Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain.
// So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols
// if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize().
void _txOnCExit()
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5 _txCleanup();
txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION);
_TX_CALLv (Win32::_cexit, ());
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnExit (int retcode)
{
if (retcode != 0)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode);
}
$5 _txCleanup();
if (retcode != 0)
txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode);
Win32::exit (retcode);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnExitProcess (unsigned retcode)
{
if (retcode != 0)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode);
}
$5 _txCleanup();
if (retcode != 0)
txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode);
Win32::ExitProcess (retcode);
}
//-----------------------------------------------------------------------------------------------------------------
bool _txOnTerminateProcess (HANDLE process, unsigned retcode)
{
if (retcode != 0)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode);
}
$5 _txCleanup();
if (retcode != 0)
txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode);
return Win32::TerminateProcess (process, retcode);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnFatalExit (int retcode)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode);
$5 _txCleanup();
txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode);
_TX_CALLv (Win32::FatalExit, (retcode));
txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode);
Win32::TerminateProcess (GetCurrentProcess(), retcode);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnFatalAppExitA (unsigned action, const char message[])
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message);
$5 _txCleanup();
txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message);
_TX_CALLv (Win32::FatalAppExitA, (action, message));
txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION);
Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
}
//-----------------------------------------------------------------------------------------------------------------
BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type);
$5 switch (type)
{
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT: $ _txExit = true;
$ _txCleanup();
case CTRL_C_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_BREAK_EVENT:
default: break;
}
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
void _txCleanup()
{
if (!_txInitialized) return;
else _txInitialized = false;
$3 _txRunning = false;
$ _txConsole_IsBlinking = false;
$ HWND canvas = txWindow();
$ HWND console = Win32::GetConsoleWindow();
$ unsigned thread = GetCurrentThreadId();
$ HWND wnd = (canvas)? canvas : console;
$ bool externTerm = (thread != _txMainThreadId &&
thread != _txCanvas_ThreadId);
$ DWORD parent = 0;
$ int isParentWaitable = _txIsParentWaitable (&parent);
$ bool waitableParent = !externTerm && isParentWaitable;
$ if (canvas)
{$ txSleep (5*_txWindowUpdateInterval); }
$ if (_txConsole)
{
$ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY);
$ if (console) EnableWindow (console, true);
}
$ if (_txMain && !externTerm && wnd != NULL)
{$ _txSetFinishedText (wnd); }
$ _flushall();
$ bool paused = false;
$ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId))
{
$ if (wnd)
{
if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); }
$ EnableWindow (wnd, true);
}
$ if (console && isParentWaitable >= 0)
{
$ _txPauseBeforeTermination (canvas);
$ paused = true;
}
}
$ if (txWindow())
{$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); }
$ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT);
$ txSpeak (NULL);
$ txPlayVideo (NULL);
$ if (GetCurrentThreadId() != _txMainThreadId)
{$ SuspendThread (_txMainThread); }
$ if (GetCurrentThreadId() != _txCanvas_ThreadId)
{$ SuspendThread (_txCanvas_Thread); }
$ if (_txMainThread)
{$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; }
$ if (_txCanvas_Thread)
{$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; }
$ if (!txWindow())
{$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; }
$ console = Win32::GetConsoleWindow();
$ if (_txMain && _txConsole)
{$ _txConsole_Detach (waitableParent && !externTerm); }
$ bool parentKilled = false;
$ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT))
{
$ parentKilled = _txKillProcess (parent);
$ parent = 0;
$ if (!parentKilled || _txIsParentWaitable (&parent))
{$ PostMessage (console, WM_CHAR, '\n', 0); }
}
$ std::cout.flush();
$ std::cerr.flush();
$ std::clog.flush();
$ _flushall();
$ _txSymGetFromAddr (NULL);
_TX_ON_DEBUG (OutputDebugString ("\n");
OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n");
OutputDebugString ("\n"));
$ if (parentKilled && _txWatchdogTimeout >= 0)
{$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); }
}
//-----------------------------------------------------------------------------------------------------------------
int _txSetFinishedText (HWND wnd)
{
struct tools
{
static LRESULT getWindowText (HWND window, wchar_t text[], size_t size)
{
$3 memset (text, 0, size * sizeof (*text));
$ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL);
}
static LRESULT setWindowText (HWND window, wchar_t text[])
{
$1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL);
}
};
$1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib";
$ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
$ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1;
$ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len);
$ tools::setWindowText (wnd, title);
$ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
$ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0;
$ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len);
$ tools::setWindowText (wnd, title);
$ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
$ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1;
$ return 2;
}
//-----------------------------------------------------------------------------------------------------------------
void _txPauseBeforeTermination (HWND canvas)
{
$3 while (_kbhit()) (void)_getch();
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
$ bool kbWait = (_txGetInput() == EOF);
$ bool wine = !!Win32::wine_get_version;
$ if (kbWait && !canvas && !kbRedir && !wine)
{
$ txSetLocale();
$ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" :
"\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects
$ fflush (stderr);
}
$ for (int i = 1; ; i++)
{
$ Sleep (_txWindowUpdateInterval);
if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide
if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something.
if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not.
if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed
if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas)))
{$ TX_ERROR ("Программа зависла и будет завершена."); break; }
if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas)))
{$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; }
if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL))
{$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; }
if (!wine && !(i % 100500))
{$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); }
}
$ while (!wine && _kbhit()) (void)_getch();
$ fprintf (stderr, "\n");
}
//-----------------------------------------------------------------------------------------------------------------
int _txGetInput()
{
$4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE);
$ DWORD nChars = 0;
$ if (GetConsoleMode (con, &nChars) == 0 &&
PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL))
{
$ return (nChars)? fgetc (stdin) : EOF;
}
$ if (_kbhit())
{
$ return _getch();
}
#if defined (_MSC_VER) && (_MSC_VER < 1700)
$ if (fseek (stdin, 1, SEEK_CUR) != EOF)
{
$ (void) fseek (stdin, -1, SEEK_CUR);
$ return fgetc (stdin); // This causes blocking in MSVC 2011 beta
}
#endif
$ return EOF;
}
//-----------------------------------------------------------------------------------------------------------------
int _txIsParentWaitable (DWORD* parentPID /*= NULL*/)
{
$4 PROCESSENTRY32* info = _txFindProcess();
$ if (!info) return 0;
$ info = _txFindProcess (info->th32ParentProcessID);
$ if (!info) return 0;
$ char parent [MAX_PATH] = "";
$ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1);
$ if (parentPID) *parentPID = info->th32ProcessID;
$ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent
$ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS;
$ char* ctx = NULL;
$ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx))
{
$ char* gp = strchr (p, ':');
$ if (gp)
{
$ *gp++ = 0;
$ if (_stricmp (p, parent) != 0) { continue; }
$ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid
{$ return islower ((unsigned char) *gp)? +1 : -1; }
}
else
{
$ if (_stricmp (p, parent) == 0)
{$ return islower ((unsigned char) *p)? +1 : -1; }
}
}
$ return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions
{
$3 if (_TX_ARGUMENT_FAILED (timeout)) return;
$ Sleep (*(int*) timeout);
$ OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection...
_TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow"));
$ DWORD parent = 0;
$ if (_txIsParentWaitable (&parent))
{
txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n",
_TX_VERSION, __func__, (unsigned long) parent);
$ _txKillProcess (parent);
$ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0);
}
txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__);
$ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Tools
//-----------------------------------------------------------------------------------------------------------------
PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/)
{
$4 static PROCESSENTRY32 info = { sizeof (info) };
$ if (!pid) return &info;
$ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
$ assert (sshot); if (!sshot) return NULL;
$ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info))
if (info.th32ProcessID == pid) break;
$ CloseHandle (sshot);
$ return &info;
}
//-----------------------------------------------------------------------------------------------------------------
// You are here, little hacker?
bool _txKillProcess (DWORD pid)
{
$3 if (_TX_ARGUMENT_FAILED (pid)) return false;
$ HANDLE token = INVALID_HANDLE_VALUE;
$ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted;
$ LUID luid = {};
$ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted;
$ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}};
$ TOKEN_PRIVILEGES old = {};
$ DWORD oldSz = 0;
$ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted;
$ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid);
$ if (!proc) return false;
$ bool ok = !!Win32::TerminateProcess (proc, 0);
$ CloseHandle (proc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
int txTaskKill (const char i[] /*= NULL*/,
const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/,
unsigned x /*= 0*/)
{
// ...so tired of it already...
#define name i // Great name!
#define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine
#define pid x // Another great name, isn't it?
$3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false;
$ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L"";
if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); }
$ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
$ assert (sshot); if (!sshot) return 0;
$ int killed = 0;
$ PROCESSENTRY32 info = { sizeof (info) };
$ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info))
{
bool kill = false;
if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; }
if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; }
if (!kill)
{
wchar_t cmdLineW[_TX_BUFSIZE] = L"";
if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; }
if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; }
}
if (kill)
{
$ if (_txKillProcess (info.th32ProcessID))
{$ killed++; }
}
}
$ CloseHandle (sshot);
$ return killed;
#undef name
#undef cmdLine
#undef pid
}
//-----------------------------------------------------------------------------------------------------------------
bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/)
{
$4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false;
$ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false;
$ if (pid == (unsigned) _getpid())
{
$ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1);
$ return true;
}
$ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
if (!proc) {$ return false; }
$ Win32::PROCESS_BASIC_INFORMATION pbi = {};
$ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted;
// Should use ReadProcessMemory() because the info is actually in another address space
$ bool ok = true;
$ Win32::PEB peb = {};
if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); }
$ Win32::RTL_USER_PROCESS_PARAMETERS params = {};
if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, ¶ms, sizeof (params), NULL); }
$ *cmdLine = 0;
if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine,
MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2),
NULL); }
$ CloseHandle (proc) asserted;
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
#define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) )
IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/)
{
$4 assert (module);
$ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0);
$ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew);
$ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE &&
ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL;
}
//-----------------------------------------------------------------------------------------------------------------
// TXLib continues to hack the reality to make your life better, sweeter and easier
uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/,
HMODULE module /*= NULL*/, bool debug /*= false*/)
{
$4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module);
$ if (_TX_ARGUMENT_FAILED (funcName)) return 0;
$ if (_TX_ARGUMENT_FAILED (newFunc)) return 0;
$ if (!module) module = GetModuleHandle (NULL);
$ if (!module) return 0;
$ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL;
$ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL;
$ if (useHotPatching && oldFunc)
{
$ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction)
$ DWORD oldRights = 0;
$ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0;
// Overwrite oldFunc prolog with JMP trampoline to newFunc.
// Calling oldFunc from any location will lead to newFunc call anyway.
$ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel
$ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF;
$ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz);
$ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights);
$ return (uintptr_t) oldFunc;
}
// For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255
// and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html.
$ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module);
if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; }
$ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
$ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset);
$ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0;
$ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL;
$ char* impDll = NULL;
$ char* impName = NULL;
$ void** impPtr = NULL;
$ bool found = false;
for (; desc->Name; desc++)
{
$ impDll = RVA_ (char*, module, desc->Name);
$ if (dllName && _stricmp (impDll, dllName) != 0) continue;
$ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk),
thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk);
thunk0 && thunk1 && thunk1->u1.Function;
thunk0++,
thunk1++)
{
impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name;
impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr
if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName);
if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) ||
(impName && _stricmp (funcName, impName) == 0))
{
found = true;
break;
}
}
$ if (found) break;
}
if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n",
funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found"));
$ if (!found) return 0;
$ DWORD rights = PAGE_READWRITE;
$ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0;
$ *(uintptr_t*) impPtr = newFunc;
$ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights);
$ return (uintptr_t) oldFunc;
}
#undef RVA_
//-----------------------------------------------------------------------------------------------------------------
bool _txInDll()
{
$4 MODULEENTRY32 mod = { sizeof (mod) };
$ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
$ assert (sshot); if (!sshot) return false;
$ bool inDll = false;
$ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod))
{
$ if (!mod.modBaseAddr) continue;
$ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr);
$ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0);
$ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize))
{$ break; }
}
$ CloseHandle (sshot);
$ return inDll;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txIsConsoleSubsystem()
{
$4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders();
$ return ntHdr &&
ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC &&
(ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI);
}
//-----------------------------------------------------------------------------------------------------------------
bool _txIsBadReadPtr (const void* address)
{
MEMORY_BASIC_INFORMATION mbi = {};
if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true;
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr
DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
return !(mbi.Protect & readRights);
}
//}
//-----------------------------------------------------------------------------------------------------------------
//! @}
//}
//=================================================================================================================
//=================================================================================================================
//{ Internal TXLib window functions (_txCanvas...)
//! @name Внутренние функции окна TXLib (_txCanvas...)
//=================================================================================================================
unsigned WINAPI _txCanvas_ThreadProc (void* data)
{
#define SetClassLong_ SetClassLongPtr
#define GCL_HICON_ GCLP_HICON
#define GCL_HICONSM_ GCLP_HICONSM
#define GCL_HCURSOR_ GCLP_HCURSOR
$8 _txCanvas_ThreadId = GetCurrentThreadId();
$ if (_TX_ARGUMENT_FAILED (data)) return false;
$ unsigned long stackSize = _TX_STACKSIZE;
$ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize));
$ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data);
$ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0;
$ HICON icon32 = LoadIcon (NULL, "_TX_ICON");
$ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM");
$ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR");
$ HMENU menu = LoadMenu (NULL, "_TX_MENU");
$ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS");
$ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32)));
$ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16)));
$ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW)));
if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); }
$ Win32::GdiSetBatchLimit (1);
_TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n"));
$ SetForegroundWindow (wnd);
$ ShowWindow (wnd, SW_SHOW);
$ UpdateWindow (wnd);
$ _txRunning = true;
$ MSG msg = {};
$ while (GetMessage (&msg, NULL, 0, 0))
{
if (!msg.hwnd) {$ continue; }
if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; }
$ TranslateMessage (&msg);
$ DispatchMessage (&msg);
$ Sleep (0);
}
$ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these
$ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak.
$ LeaveCriticalSection (&_txCanvas_LockBackBuf);
_TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n"));
$ if (_txWatchdogTimeout >= 0)
{$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); }
$ if (_txRunning && _txMain) // Main window is destroyed but main() is still running.
{ // No chances for good termination, so use exit().
$ _txCleanup();
$ ::exit ((int) msg.wParam);
}
$ _txCanvas_ThreadId = 0;
$ return true;
#undef SetClassLong
#undef GCL_HICON_
#undef GCL_HICONSM_
#undef GCL_HCURSOR_
}
//-----------------------------------------------------------------------------------------------------------------
HWND _txCanvas_CreateWindow (const SIZE* sizePtr)
{
$8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL;
$ bool centered = false;
if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; }
$ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) };
$ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false);
$ SIZE size = { rect.right - rect.left, rect.bottom - rect.top };
$ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0);
$ if (!wndClass) return (HWND) NULL;
$ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN,
centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT,
centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT,
size.cx, size.cy, NULL, NULL, NULL, NULL);
$ if (!wnd || !txWindow())
{$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; }
$ HMENU menu = GetSystemMenu (txWindow(), false);
if (!menu) {$ return txWindow(); }
$ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted;
$ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted;
$ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted;
$ return txWindow();
}
//-----------------------------------------------------------------------------------------------------------------
const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra)
{
$8 assert (classId);
$ assert (wndProc);
$ static char name[_TX_BUFSIZE] = "";
$ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ "
_TX_VERSION " " __FILE__ " WndClass %08lX "
"-------------[%s]-[TXLib]---*/",
classId, (unsigned long) GetTickCount(), classId);
$ WNDCLASS wc = { sizeof (wc) };
$ wc.lpszClassName = name;
$ wc.lpfnWndProc = wndProc;
$ wc.style = style;
$ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long);
$ wc.hCursor = LoadCursor (NULL, IDC_ARROW);
$ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush);
$ ATOM atom = RegisterClass (&wc);
if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; }
$ return (const char*)(uintptr_t) atom;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool _txCanvas_OK()
{
return _txCanvas_ThreadId &&
_txCanvas_Window &&
_txCanvas_BackBuf[0] &&
_txCanvas_BackBuf[1] &&
_txCanvas_Pixels;
}
//-----------------------------------------------------------------------------------------------------------------
int _txCanvas_SetRefreshLock (int count)
{
$8 int oldCount = _txCanvas_RefreshLock;
$ _txCanvas_RefreshLock = count;
$ HWND wnd = txWindow();
$ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd)
{$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
$ return oldCount;
}
//-----------------------------------------------------------------------------------------------------------------
HICON _txCreateTXIcon (int size)
{
$8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL;
$ const unsigned char image32 [32*32+1] =
"00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0"
"0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0"
"0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0"
"0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0"
"0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0"
"0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0"
"0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0"
"0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000";
$ const unsigned char image16 [16*16+1] =
"0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990"
"0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000";
$ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0,
0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff };
$ const unsigned char* image = (size == 32)? image32 : image16;
$ POINT sz = { size, size };
$ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask);
$ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor);
$ for (int i = 0; i < size*size; i++)
{
assert (In (std::nomeow, image[i], '0', '9') ||
In (std::nomeow, image[i], 'A', 'F'));
Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']);
}
$ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP),
(HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) };
$ HICON icon = CreateIconIndirect (&info);
$ assert (icon);
$ _txBuffer_Delete (&dcMask) asserted;
$ _txBuffer_Delete (&dcColor) asserted;
$ return icon;
}
//}
//=================================================================================================================
//=================================================================================================================
//{ Main window event handlers (_txCanvas_On...)
//! @name События основного окна (_txCanvas_On...)
//=================================================================================================================
//! @{
LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
{
#if defined (_TX_ALLOW_TRACE)
int inTX = _txLoc::Cur.inTX++;
if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)",
2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar);
_txLoc::Cur.inTX = inTX;
#endif
$8 if (msg == WM_KEYDOWN && wpar == VK_F12 &&
GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU))
{
$ _txCanvas_OnCmdABOUT (wnd, wpar);
$ return DefWindowProc (wnd, msg, wpar, lpar);
}
WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread
if (altWndProc)
{
$ LRESULT res = altWndProc (wnd, msg, wpar, lpar);
$ if (res) return res;
}
static bool bkErased = false;
switch (msg)
{
case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; }
case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; }
case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; }
case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; }
case WM_SIZE: {$ bkErased = false; break; }
case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; }
case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; }
case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; }
case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; }
case WM_LBUTTONUP:
case WM_LBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; }
case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; }
case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; }
case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; }
default: break;
}
if (msg == WM_SYSCOMMAND) switch (wpar)
{
case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; }
case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; }
default: break;
}
$ return DefWindowProc (wnd, msg, wpar, lpar);
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCREATE (HWND wnd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]);
$ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]);
$ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0;
$ assert (_txCanvas_RefreshTimer);
$ _txCanvas_UserDCs = new ::std::vector <HDC>;
$ _txCanvas_Window = wnd;
$ txSetDefaults();
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnDESTROY (HWND wnd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
// Инициируем остановку цикла сообщений
$ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS);
$ if (!_txCanvas_Window) return false;
// Indicate that we are about to manually terminate
$ _txExit = true;
// Lock GDI resources
$ bool locked = false;
$ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT);
$ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources");
// Освобождаем пользовательские ресурсы
$ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty())
{
$ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size());
$ Sleep (_TX_TIMEOUT);
$ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i));
$ _txCanvas_UserDCs->clear();
}
$ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL;
// Освобождаем ресурсы, связанные с окном
$ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted;
$ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted;
$ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted;
$ _txCanvas_Pixels = NULL;
$ txUnlock();
// Indicate that we are destroyed
$ _txCanvas_Window = NULL;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCLOSE (HWND wnd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ if (!_txCanvas_OK()) return false;
$ if (_txMain && _txRunning &&
txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n"
"Лучше подождать, когда main() завершится - это отображается в заголовке окна.",
txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnTIMER (HWND wnd, WPARAM)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false;
$ InvalidateRect (wnd, NULL, false) asserted;
$ UpdateWindow (wnd) asserted;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnPAINT (HWND wnd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ if (!_txCanvas_OK()) return false;
$ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) &&
GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT);
$ PAINTSTRUCT ps = {};
$ HDC wndDc = BeginPaint (wnd, &ps);
$ if (!wndDc) return false;
$ HDC dc0 = _txCanvas_BackBuf[0],
dc1 = _txCanvas_BackBuf[1];
$ RECT r = {};
$ GetClientRect (wnd, &r) asserted;
$ POINT wndSize = { r.right - r.left, r.bottom - r.top };
$ POINT dcSize = txGetExtent (dc1);
$ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) &&
txLock (false))
{
$ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY);
$ _txConsole_Draw (dc1);
$ txUnlock();
}
// Magic 100500 value is used to completely block screen refresh.
// Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL.
// Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers
// themselves.
// Yes guys, with all your software installed. :(
$ if (_txCanvas_RefreshLock != 100500)
{
if (_txSwapBuffers)
{
$ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY);
}
else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y)
{
$ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY);
}
else
{
$ Win32::SetStretchBltMode (wndDc, HALFTONE);
$ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY);
}
}
$ EndPaint (wnd, &ps) asserted;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info)
{
$8 INPUT_RECORD evt = {};
$ evt.EventType = KEY_EVENT;
$ evt.Event.KeyEvent.bKeyDown = true;
$ evt.Event.KeyEvent.wRepeatCount = 1;
$ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR
$ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16);
$ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk;
$ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0;
$ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job
$ DWORD written = 0;
$ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written);
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info)
{
$8 INPUT_RECORD evt = {};
$ evt.EventType = KEY_EVENT;
$ evt.Event.KeyEvent.bKeyDown = true;
$ evt.Event.KeyEvent.wRepeatCount = 1;
$ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch);
$ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16);
$ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX
$ evt.Event.KeyEvent.dwControlKeyState = 0;
$ DWORD written = 0;
$ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written);
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ if (!_txCanvas_OK()) return false;
$ if (_txMousePos.x == -1 && _txMousePos.y == -1)
{
$ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT };
$ TrackMouseEvent (&track);
}
$ _txMousePos.x = LOWORD (coords);
$ _txMousePos.y = HIWORD (coords);
$ _txMouseButtons = (unsigned) buttons;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnMOUSELEAVE (HWND)
{
$8 _txMousePos.x = -1;
$ _txMousePos.y = -1;
$ _txMouseButtons = 0;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar)
{
$8 if (_TX_ARGUMENT_FAILED (lpar)) return false;
$ const CREATESTRUCT* create = (CREATESTRUCT*) lpar;
$ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style,
create->x, create->y, create->cx, create->cy,
create->hwndParent, create->hMenu, NULL, create->lpCreateParams);
$ *(HWND*) create->hInstance = wnd;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar)
{
$8 if (_TX_ARGUMENT_FAILED (lpar)) return false;
$ DestroyWindow ((HWND) lpar);
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ HWND console = Win32::GetConsoleWindow();
$ if (!console) return false;
$ bool visible = !!IsWindowVisible (console);
$ ShowWindow (console, visible? SW_HIDE : SW_SHOW);
$ visible = !!IsWindowVisible (console);
$ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED);
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCmdABOUT (HWND, WPARAM)
{
$8 //{ Overriding the missing names, if the set is uncomplete
#if defined (__MODULE)
#define ABOUT_NAME_ __MODULE
#else
#define ABOUT_NAME_ "TXLib"
#endif
#if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR)
#ifndef __MODULE
#define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n"
#endif
#ifndef __VERSION
#define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n"
#endif
#ifndef __DESCRIPTION
#define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n"
#endif
#ifndef __AUTHOR
#define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name."
#endif
#endif
//}
$ static char text[_TX_BUFSIZE] = "";
$ _tx_snprintf_s (text, sizeof (text) - 1,
"Application:\n\n"
#if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR)
__MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n"
#else
"Здесь могла бы быть Ваша реклама :)\n"
"#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n"
#endif
"\n" "%s", _txAppInfo());
$ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION);
// And a bit of HTTP-code in C++ function:
goto http;
http://sizeof.livejournal.com
$ return true;
#undef ABOUT_NAME_
}
//! @}
//}
//=================================================================================================================
//=================================================================================================================
//{ Console-support functions (_txConsole...)
//! @name Функции консольного окна (_txConsole...)
//=================================================================================================================
//! @{
HWND _txConsole_Attach()
{
$1 HWND console = Win32::GetConsoleWindow();
$ if (!console)
{
$ FreeConsole();
$ AllocConsole();
}
$ console = Win32::GetConsoleWindow();
$ if (!console) return NULL;
$ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows
$ static bool done = false;
$ if (done) return console;
$ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть.
$ if (!_txIsConsoleSubsystem())
{$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console
// That's all, folks
$ done = true;
$ return console;
}
//-----------------------------------------------------------------------------------------------------------------
int txSetLocale (int codepage /*= _TX_CODEPAGE*/,
const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/)
{
$1 int oldPage = GetConsoleOutputCP();
// Устанавливаем нужную кодовую страницу для консоли Windows
$ if (codepage)
{
$ SetConsoleCP (codepage);
$ SetConsoleOutputCP (codepage);
}
// Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии
// функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным
// языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN -
// обозначение кодовой страницы (например, для русского языка - CP1251).
$ if (locale)
{
$ setlocale (LC_ALL, locale);
$ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers
}
#ifndef __CYGWIN__
$ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility.
$ if (wLocale && !wine)
{
$ _wsetlocale (LC_ALL, wLocale);
$ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above)
}
#endif
(void) wLocale;
$ return oldPage;
}
//-----------------------------------------------------------------------------------------------------------------
void txReopenStdio()
{
$1 // Переоткрываем заново <s>Америку) [0])
7907 case 192: {$ codePage = 1251; sCodePage =
"1251.";
break; }
7908 case 208: {$ codePage = 65001; sCodePage =
"UTF-8.";
break; }
7909 case 128: {$ codePage = 866; sCodePage =
"866.";
break; }
7910 case 225: {$ codePage = 20866; sCodePage =
"KOI-8, waaat?!";
break; }
7911 default: {$ codePage = -1; sCodePage =
"(Unknown)";
break; }
7914 $
if (codePage != needCP && verbose)
7916 $ *_txTraceSE =
' ';
7918 $ _TX_UNEXPECTED (
"\v\t" "\n\n" "WARNING: CHECK TXLib.h file CODEPAGE. Maybe it is %s It should be %d.\n\n"
7919 "This is NOT an error of TXLib itself. Please note:\n\n"
7920 "Do NOT copy-and-paste TXLib.h file contents into a new file and them save it inside your "
7921 "IDE or editor. This can change original TXLib codepage (%d) to another one. Instead, DO "
7922 "use copy / move / cut-and-paste operations in Windows Explorer (Far Manager etc) only. "
7923 "Or, when you see TXLib.h being opened in browser, use 'Save as...' (Ctrl+S) command.\n\n"
7924 "Now you should re-download TXLib.h file from the http://txlib.ru site.\n\n"
7925 "You can continue, but Russian messages and symbols may appear unreadable.",
7926 sCodePage, needCP, needCP);
7929 $
return (codePage == needCP);
7934 _tx_FARPROC _txDllImport (
const char dllFileName[],
const char funcName[],
bool required )
7936 if (_TX_ARGUMENT_FAILED (dllFileName && *dllFileName))
return NULL;
7937 if (_TX_ARGUMENT_FAILED (funcName && *funcName))
return NULL;
7939 static char dllPaths [2][MAX_PATH] = {
"",
""};
7943 const char dllDir[] =
"\\Windows\\";
7947 char* path = dllPaths[0];
7949 txRegQuery (
"HKCU\\Software\\TX Library",
"ProductDir", path, MAX_PATH);
7950 strncat_s (path, MAX_PATH, dllDir,
sizeof (dllDir) - 1);
7956 if (strchr (__FILE__,
':'))
7958 strncpy_s (path, MAX_PATH, __FILE__,
sizeof (__FILE__) - 1);
7962 GetCurrentDirectory (MAX_PATH, path);
7963 strncat_s (path, MAX_PATH,
"\\" __FILE__,
sizeof (
"\\" __FILE__) - 1);
7966 if (
char* dir = strrchr (path,
'\\')) *dir = 0;
7968 strncat_s (path, MAX_PATH, dllDir,
sizeof (dllDir) - 1);
7971 char dllName[MAX_PATH] =
"", dllArch[MAX_PATH] =
"";
7972 const char* arch = (dllFileName? strchr (dllFileName,
'*') : NULL);
7976 assert (arch >= dllFileName);
7978 strncpy_s (dllName,
sizeof (dllName), dllFileName, (
size_t) (arch - dllFileName));
7979 strncat_s (dllName,
sizeof (dllName), arch+1,
sizeof (dllName) - 1 - strlen (dllName));
7981 strncpy_s (dllArch,
sizeof (dllArch), dllFileName, (
size_t) (arch - dllFileName));
7982 strncat_s (dllArch,
sizeof (dllArch),
sizeof (
void*) == 8?
"64" :
"32", 3);
7983 strncat_s (dllArch,
sizeof (dllArch), arch+1,
sizeof (dllArch) - 1 - strlen (dllArch));
7985 else if (dllFileName)
7987 strncat_s (dllName,
sizeof (dllName), dllFileName,
sizeof (dllName) - 1);
7990 HMODULE dll = GetModuleHandle (dllFileName);
7992 if (!dll) dll = GetModuleHandle (dllArch);
7993 if (!dll) dll = GetModuleHandle (dllName);
7995 for (
int i = 0; !dll && i < (int)
sizearr (dllPaths); i++)
7997 char path [MAX_PATH] =
"";
7998 strncpy_s (path,
sizeof (path), dllPaths[i],
sizeof (dllPaths[i]));
7999 size_t len = strlen (path);
8001 strncpy_s (path + len,
sizeof (path) - len, dllArch,
sizeof (dllArch));
8002 if (!dll) dll = LoadLibrary (path);
8004 strncpy_s (path + len,
sizeof (path) - len, dllName,
sizeof (dllName));
8005 if (!dll) dll = LoadLibrary (path);
8008 if (!dll) dll = LoadLibrary (dllArch);
8009 if (!dll) dll = LoadLibrary (dllName);
8011 if (!dll && required)
TX_ERROR (
"\a" "Cannot load library \"%s%s%s\".",
8012 dllName, (arch?
"\" / \"" :
""), dllArch);
8013 if (!dll)
return NULL;
8015 _tx_FARPROC addr = (_tx_FARPROC) GetProcAddress (dll, funcName);
8017 if (!addr && required)
TX_ERROR (
"\a" "Cannot import \"%s\" from library \"%s%s%s\".",
8018 funcName, dllName, (arch?
"\" / \"" :
""), dllArch);
8024 #if defined (_MSC_VER) && (_MSC_VER == 1800) // MSVC 2013
8025 #pragma warning (push)
8026 #pragma warning (disable: 6102) // Использование 'name' из завершившегося ошибкой вызова функции #endif
int txRegQuery (const char* keyName, const char* valueName, void* value, size_t szValue)
{
if (_TX_ARGUMENT_FAILED (keyName)) return 0;
HKEY hive = NULL;
#define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \
_strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 )
if (EQU_("HKLM", "HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE;
else if (EQU_("HKCU", "HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER;
else if (EQU_("HKCR", "HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT;
else if (EQU_("HKU", "HKEY_USERS")) hive = HKEY_USERS;
else if (EQU_("HKCC", "HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG;
else { _TX_ARGUMENT_FAILED (("keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive)); return 0; }
#undef EQU_
keyName = strchr (keyName, '\\') + 1;
HKEY key = NULL;
DWORD size = 0;
bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS;
if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS;
if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS;
if (key) ok &= !!RegCloseKey (key);
return size;
}
#if defined (_MSC_VER) && (_MSC_VER == 1800)
#pragma warning (pop)
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
HWND txCreateWindow (double sizeX, double sizeY, bool centered /*= true*/)
{
$1 if (!_txInitialized) _txInitialized = _txInitialize();
$ if (HWND wnd = txWindow())
{
$ SetLastErrorEx (ERROR_INVALID_DATA, 0);
$ _TX_ON_DEBUG (TX_ERROR ("\a" "Окно рисования уже создано!"));
$ return wnd;
}
$ if (!_txIsDll)
{
$ _txMain = ! FindAtom ("_txMain"); // Not a thread-safe
$ (void) AddAtom ("_txMain");
}
$ if (_txWindowUpdateInterval < 10) {$ _txWindowUpdateInterval = 10; }
$ _txRunning = false;
// Store the size
$ static SIZE size = { ROUND (sizeX), ROUND (sizeY) };
$ if (centered) { size.cx *= -1; size.cy *= -1; }
// In Thread, where REAL creation lies...
$ unsigned id = 0;
$ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &id);
$ if (!_txCanvas_Thread) return TX_DEBUG_ERROR ("\a" "Cannot start canvas thread."), (HWND) NULL;
$ _txWaitFor (_txRunning, 10*_TX_TIMEOUT);
$ if (!_txRunning) return TX_DEBUG_ERROR ("\a" "Cannot create canvas window."), (HWND) NULL;
$ if (!txOK()) return TX_DEBUG_ERROR ("\a" "Canvas window is not OK."), (HWND) NULL;
$ HWND console = Win32::GetConsoleWindow();
$ DWORD proc = 0;
$ GetWindowThreadProcessId (console, &proc);
$ if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable()))
{$ ShowWindow (console, _txConsoleMode); }
$ HMENU menu = GetSystemMenu (txWindow(), false);
if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); }
$ Win32::GdiSetBatchLimit (1);
$ SetLastError (0);
$ errno = 0;
#if !defined (__CYGWIN__)
$ _doserrno = 0;
#endif
$ return txWindow();
}
//-----------------------------------------------------------------------------------------------------------------
HWND txCreateExtraWindow (CREATESTRUCT createData)
{
$1 if (_TX_TXWINDOW_FAILED()) return NULL;
$ volatile HWND wnd = NULL;
$ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd;
$ PostMessage (txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData) asserted;
$ _txWaitFor (wnd, 5*_TX_TIMEOUT);
$ return wnd;
}
//-----------------------------------------------------------------------------------------------------------------
bool txSetDefaults (HDC dc /*= txDC()*/)
{
$1 if (dc == txDC()) txUpdateWindow (false);
$ txAutoLock _lock;
$ RECT r = {};
$ GetClientRect (Win32::GetConsoleWindow(), &r) asserted;
$ SIZE szCon = { r.right - r.left, r.bottom - r.top };
$ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
$ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}};
$ GetConsoleScreenBufferInfo (out, &con);
$ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1),
(short) (con.srWindow.Bottom - con.srWindow.Top + 1) };
//{ Set defaults for graphics layer
$ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc) asserted;
$ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc) asserted;
$ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx,
0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont),
dc) asserted;
$ (Win32::SetTextColor (dc, TX_WHITE) != CLR_INVALID) asserted;
$ Win32::SetBkMode (dc, TRANSPARENT) asserted;
$ Win32::SetROP2 (dc, R2_COPYPEN) asserted;
$ Win32::SetStretchBltMode (dc, HALFTONE) asserted;
//}
$ if (dc != txDC())
{$ return true; }
//{ Set defaults for console layer
$ POINT szCanvas = txGetExtent (dc);
$ HGDIOBJ font = txFontExist (_txConsoleFont)?
Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx,
0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, FIXED_PITCH, _txConsoleFont)
:
Win32::GetStockObject (SYSTEM_FIXED_FONT);
$ _txBuffer_Select (font, _txCanvas_BackBuf[1]);
//}
//{ Scroll the console for text to go above top of window and don't mix with graphics
$ if (con.dwCursorPosition.X) _putch ('\n');
$ short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top);
$ con.srWindow.Top = (short) (con.srWindow.Top + delta);
$ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta);
$ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) };
$ CHAR_INFO fill = { {' '}, FOREGROUND_LIGHTGRAY };
$ COORD dest = { 0, (short) -delta }; // New UL-corner of src, scroll up
$ con.dwCursorPosition.X = 0;
$ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta);
$ (con.srWindow.Bottom < con.dwSize.Y && // Move the "window"
SetConsoleWindowInfo (out, true, &con.srWindow))
||
(ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill), // Or scroll the buffer
SetConsoleCursorPosition (out, con.dwCursorPosition));
//}
$ txUpdateWindow (true);
return true;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txOK()
{
return (_txCanaryFirst == 0x776F656D && // Too well-known values to use constants. You know these values, don't you?
_txCanaryLast == 0x5E2E2E5E &&
_txCanvas_OK()
#if defined (_MSC_VER)
&& _CrtCheckMemory()
#endif
);
}
//-----------------------------------------------------------------------------------------------------------------
//{ Cleanup
//-----------------------------------------------------------------------------------------------------------------
// Implicit std(MSVCRT.dll)::_cexit() call before _txCleanup can lead to hangs in _cexit handlers chain.
// So redefining ::std::_cexit(). Do it dynamically via PE Import Table hook to avoid duplicate symbols
// if several modules linked together include TXLib.h. See _txSetProcAddress() call in _txInitialize().
void _txOnCExit()
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5 _txCleanup();
txOutputDebugPrintf ("%s - WARNING: calling Win32::_cexit()\n", _TX_VERSION);
_TX_CALLv (Win32::_cexit, ());
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnExit (int retcode)
{
if (retcode != 0)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode);
}
$5 _txCleanup();
if (retcode != 0)
txOutputDebugPrintf ("%s - WARNING: calling Win32::exit (%d)\n", _TX_VERSION, retcode);
Win32::exit (retcode);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnExitProcess (unsigned retcode)
{
if (retcode != 0)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (%u) called\n", _TX_VERSION, __func__, retcode);
}
$5 _txCleanup();
if (retcode != 0)
txOutputDebugPrintf ("%s - WARNING: calling Win32::ExitProcess (%u)\n", _TX_VERSION, retcode);
Win32::ExitProcess (retcode);
}
//-----------------------------------------------------------------------------------------------------------------
bool _txOnTerminateProcess (HANDLE process, unsigned retcode)
{
if (retcode != 0)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (0x%p, %u) called\n", _TX_VERSION, __func__, process, retcode);
}
$5 _txCleanup();
if (retcode != 0)
txOutputDebugPrintf ("%s - WARNING: calling Win32::TerminateProcess (0x%p, %u)\n", _TX_VERSION, process, retcode);
return Win32::TerminateProcess (process, retcode);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnFatalExit (int retcode)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (%d) called\n", _TX_VERSION, __func__, retcode);
$5 _txCleanup();
txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalExit (%d)\n", _TX_VERSION, retcode);
_TX_CALLv (Win32::FatalExit, (retcode));
txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (%d)\n", _TX_VERSION, retcode);
Win32::TerminateProcess (GetCurrentProcess(), retcode);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnFatalAppExitA (unsigned action, const char message[])
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (%u, \"%s\") called\n", _TX_VERSION, __func__, action, message);
$5 _txCleanup();
txOutputDebugPrintf ("%s - WARNING: calling Win32::FatalAppExitA (%u, %s)\n", _TX_VERSION, action, message);
_TX_CALLv (Win32::FatalAppExitA, (action, message));
txOutputDebugPrintf ("%s - WARNING: Win32::FatalExit() failure, calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION);
Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
}
//-----------------------------------------------------------------------------------------------------------------
BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type)
{
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s (0x%04lX) called\n", _TX_VERSION, __func__, (unsigned long) type);
$5 switch (type)
{
case CTRL_LOGOFF_EVENT:
case CTRL_SHUTDOWN_EVENT: $ _txExit = true;
$ _txCleanup();
case CTRL_C_EVENT:
case CTRL_CLOSE_EVENT:
case CTRL_BREAK_EVENT:
default: break;
}
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
void _txCleanup()
{
if (!_txInitialized) return;
else _txInitialized = false;
$3 _txRunning = false;
$ _txConsole_IsBlinking = false;
$ HWND canvas = txWindow();
$ HWND console = Win32::GetConsoleWindow();
$ unsigned thread = GetCurrentThreadId();
$ HWND wnd = (canvas)? canvas : console;
$ bool externTerm = (thread != _txMainThreadId &&
thread != _txCanvas_ThreadId);
$ DWORD parent = 0;
$ int isParentWaitable = _txIsParentWaitable (&parent);
$ bool waitableParent = !externTerm && isParentWaitable;
$ if (canvas)
{$ txSleep (5*_txWindowUpdateInterval); }
$ if (_txConsole)
{
$ if (_txMain) txSetConsoleAttr (FOREGROUND_LIGHTGRAY);
$ if (console) EnableWindow (console, true);
}
$ if (_txMain && !externTerm && wnd != NULL)
{$ _txSetFinishedText (wnd); }
$ _flushall();
$ bool paused = false;
$ if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId))
{
$ if (wnd)
{
if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); }
$ EnableWindow (wnd, true);
}
$ if (console && isParentWaitable >= 0)
{
$ _txPauseBeforeTermination (canvas);
$ paused = true;
}
}
$ if (txWindow())
{$ SendNotifyMessage (txWindow(), WM_DESTROY, 0, 0); }
$ _txWaitFor (!txWindow(), 5*_TX_TIMEOUT);
$ txSpeak (NULL);
$ txPlayVideo (NULL);
$ if (GetCurrentThreadId() != _txMainThreadId)
{$ SuspendThread (_txMainThread); }
$ if (GetCurrentThreadId() != _txCanvas_ThreadId)
{$ SuspendThread (_txCanvas_Thread); }
$ if (_txMainThread)
{$ CloseHandle (_txMainThread) asserted; _txMainThread = NULL; }
$ if (_txCanvas_Thread)
{$ CloseHandle (_txCanvas_Thread) asserted; _txCanvas_Thread = NULL; }
$ if (!txWindow())
{$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; }
$ console = Win32::GetConsoleWindow();
$ if (_txMain && _txConsole)
{$ _txConsole_Detach (waitableParent && !externTerm); }
$ bool parentKilled = false;
$ if (waitableParent && paused && _txNOP (_TX_ALLOW_KILL_PARENT))
{
$ parentKilled = _txKillProcess (parent);
$ parent = 0;
$ if (!parentKilled || _txIsParentWaitable (&parent))
{$ PostMessage (console, WM_CHAR, '\n', 0); }
}
$ std::cout.flush();
$ std::cerr.flush();
$ std::clog.flush();
$ _flushall();
$ _txSymGetFromAddr (NULL);
_TX_ON_DEBUG (OutputDebugString ("\n");
OutputDebugString (_TX_VERSION " - FINISHED: " _TX_MODULE "\n");
OutputDebugString ("\n"));
$ if (parentKilled && _txWatchdogTimeout >= 0)
{$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); }
}
//-----------------------------------------------------------------------------------------------------------------
int _txSetFinishedText (HWND wnd)
{
struct tools
{
static LRESULT getWindowText (HWND window, wchar_t text[], size_t size)
{
$3 memset (text, 0, size * sizeof (*text));
$ return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL);
}
static LRESULT setWindowText (HWND window, wchar_t text[])
{
$1 return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL);
}
};
$1 static wchar_t title [_TX_BUFSIZE+15] = L"TXLib";
$ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
$ int len = (int) wcslen (title); if (len >= (int)_TX_BUFSIZE) len = _TX_BUFSIZE-1;
$ MultiByteToWideChar (_TX_CODEPAGE, 0, " [ЗАВЕРШЕНО]", -1, title + len, (int)_TX_BUFSIZE - len);
$ tools::setWindowText (wnd, title);
$ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
$ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == (wchar_t) 0x0417 /* 'З' */) return 0;
$ MultiByteToWideChar (_TX_CODEPAGE, 0, " [FINISHED]", -1, title + len, (int)_TX_BUFSIZE - len);
$ tools::setWindowText (wnd, title);
$ tools::getWindowText (wnd, title, _TX_BUFSIZE-1);
$ if (len <= (int)_TX_BUFSIZE-1-2 && title [len+2] == /* 'F' */ (wchar_t) 0x0046) return 1;
$ return 2;
}
//-----------------------------------------------------------------------------------------------------------------
void _txPauseBeforeTermination (HWND canvas)
{
$3 while (_kbhit()) (void)_getch();
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
$ bool kbWait = (_txGetInput() == EOF);
$ bool wine = !!Win32::wine_get_version;
$ if (kbWait && !canvas && !kbRedir && !wine)
{
$ txSetLocale();
$ fprintf (stderr, (!_txErrors? "\n" "[Нажмите любую клавишу для завершения]" :
"\n" "[Press F to Pay Respects...]")); // https://knowyourmeme.com/memes/press-f-to-pay-respects
$ fflush (stderr);
}
$ for (int i = 1; ; i++)
{
$ Sleep (_txWindowUpdateInterval);
if (!kbWait || (kbRedir && !canvas)) {$ break; } // No need to run and hide
if (!wine && _txGetInput() != EOF) {$ break; } // Somebody hit something.
if (canvas && !_txCanvas_ThreadId) {$ break; } // There was a window, and now there is not.
if (!Win32::GetConsoleWindow()) {$ break; } // Console was destroyed
if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas)))
{$ TX_ERROR ("Программа зависла и будет завершена."); break; }
if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas)))
{$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа-таки зависла и будет завершена."); break; }
if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG, _TX_TIMEOUT, NULL))
{$ _txTrace (__FILE__, __LINE__, NULL, "WARNING: Программа не отвечает и будет завершена."); break; }
if (!wine && !(i % 100500))
{$ fprintf (stderr, "\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); }
}
$ while (!wine && _kbhit()) (void)_getch();
$ fprintf (stderr, "\n");
}
//-----------------------------------------------------------------------------------------------------------------
int _txGetInput()
{
$4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE);
$ DWORD nChars = 0;
$ if (GetConsoleMode (con, &nChars) == 0 &&
PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL))
{
$ return (nChars)? fgetc (stdin) : EOF;
}
$ if (_kbhit())
{
$ return _getch();
}
#if defined (_MSC_VER) && (_MSC_VER < 1700)
$ if (fseek (stdin, 1, SEEK_CUR) != EOF)
{
$ (void) fseek (stdin, -1, SEEK_CUR);
$ return fgetc (stdin); // This causes blocking in MSVC 2011 beta
}
#endif
$ return EOF;
}
//-----------------------------------------------------------------------------------------------------------------
int _txIsParentWaitable (DWORD* parentPID /*= NULL*/)
{
$4 PROCESSENTRY32* info = _txFindProcess();
$ if (!info) return 0;
$ info = _txFindProcess (info->th32ParentProcessID);
$ if (!info) return 0;
$ char parent [MAX_PATH] = "";
$ strncpy_s (parent, sizeof (parent), info->szExeFile, sizeof (parent) - 1);
$ if (parentPID) *parentPID = info->th32ProcessID;
$ info = _txFindProcess (info->th32ParentProcessID); // info: grandparent
$ char list[_TX_BUFSIZE] = _TX_WAITABLE_PARENTS;
$ char* ctx = NULL;
$ for (char* p = strtok_s (list, ", ", &ctx); p; p = strtok_s (NULL, ", ", &ctx))
{
$ char* gp = strchr (p, ':');
$ if (gp)
{
$ *gp++ = 0;
$ if (_stricmp (p, parent) != 0) { continue; }
$ if (info) if (_stricmp (gp, info->szExeFile) == 0) // Was &&, but MSVC /analyze is so paranoid
{$ return islower ((unsigned char) *gp)? +1 : -1; }
}
else
{
$ if (_stricmp (p, parent) == 0)
{$ return islower ((unsigned char) *p)? +1 : -1; }
}
}
$ return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txWatchdogTerminator (void* timeout) // Or Watchcat? Possibly will change in future versions
{
$3 if (_TX_ARGUMENT_FAILED (timeout)) return;
$ Sleep (*(int*) timeout);
$ OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: %s(): Timeout (%d) expired, activating. %s\n", // Kinda static reflection...
_TX_VERSION, __func__, *(int*) timeout, ((__func__[8] == 'd')? "Bark, bark" : "Meow, meow"));
$ DWORD parent = 0;
$ if (_txIsParentWaitable (&parent))
{
txOutputDebugPrintf ("%s - WARNING: %s(): Calling _txKillProcess (0x%04lu)\n",
_TX_VERSION, __func__, (unsigned long) parent);
$ _txKillProcess (parent);
$ PostMessage (GetConsoleWindow(), WM_CHAR, '\n', 0);
}
txOutputDebugPrintf ("%s - WARNING: %s(): Calling Win32::TerminateProcess (EXIT_FAILURE)\n", _TX_VERSION, __func__);
$ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Tools
//-----------------------------------------------------------------------------------------------------------------
PROCESSENTRY32* _txFindProcess (unsigned pid /*= GetCurrentProcessId()*/)
{
$4 static PROCESSENTRY32 info = { sizeof (info) };
$ if (!pid) return &info;
$ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
$ assert (sshot); if (!sshot) return NULL;
$ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info))
if (info.th32ProcessID == pid) break;
$ CloseHandle (sshot);
$ return &info;
}
//-----------------------------------------------------------------------------------------------------------------
// You are here, little hacker?
bool _txKillProcess (DWORD pid)
{
$3 if (_TX_ARGUMENT_FAILED (pid)) return false;
$ HANDLE token = INVALID_HANDLE_VALUE;
$ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token) asserted;
$ LUID luid = {};
$ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid) asserted;
$ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}};
$ TOKEN_PRIVILEGES old = {};
$ DWORD oldSz = 0;
$ AdjustTokenPrivileges (token, false, &priv, sizeof (priv), &old, &oldSz) asserted;
$ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid);
$ if (!proc) return false;
$ bool ok = !!Win32::TerminateProcess (proc, 0);
$ CloseHandle (proc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
int txTaskKill (const char i[] /*= NULL*/,
const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] /*= NULL*/,
unsigned x /*= 0*/)
{
// ...so tired of it already...
#define name i // Great name!
#define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine
#define pid x // Another great name, isn't it?
$3 if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) && "Вот такие тут интересные имена встречаются...")) return false;
$ wchar_t cmdLineSubstrW[_TX_BUFSIZE] = L"";
if (cmdLineSubstr) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW, sizearr (cmdLineSubstrW)); }
$ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
$ assert (sshot); if (!sshot) return 0;
$ int killed = 0;
$ PROCESSENTRY32 info = { sizeof (info) };
$ for (bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info))
{
bool kill = false;
if (!kill && pid && info.th32ParentProcessID == pid) {$ kill = true; }
if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill = true; }
if (!kill)
{
wchar_t cmdLineW[_TX_BUFSIZE] = L"";
if (!_txGetCommandLine (cmdLineW, sizearr (cmdLineW), info.th32ProcessID)) { continue; }
if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill = true; }
}
if (kill)
{
$ if (_txKillProcess (info.th32ProcessID))
{$ killed++; }
}
}
$ CloseHandle (sshot);
$ return killed;
#undef name
#undef cmdLine
#undef pid
}
//-----------------------------------------------------------------------------------------------------------------
bool _txGetCommandLine (wchar_t cmdLine[], size_t szCmdLine, unsigned pid /*= _getpid()*/)
{
$4 if (_TX_ARGUMENT_FAILED (cmdLine)) return false;
$ if (_TX_ARGUMENT_FAILED (szCmdLine >= 2)) return false;
$ if (pid == (unsigned) _getpid())
{
$ wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1);
$ return true;
}
$ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
if (!proc) {$ return false; }
$ Win32::PROCESS_BASIC_INFORMATION pbi = {};
$ Win32::NtQueryInformationProcess (proc, 0 /*ProcessBasicInformation*/, &pbi, sizeof (pbi), NULL) == 0 asserted;
// Should use ReadProcessMemory() because the info is actually in another address space
$ bool ok = true;
$ Win32::PEB peb = {};
if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); }
$ Win32::RTL_USER_PROCESS_PARAMETERS params = {};
if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, ¶ms, sizeof (params), NULL); }
$ *cmdLine = 0;
if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine,
MIN (params.CommandLine.Length + 2, (int) (szCmdLine * sizeof (*cmdLine)) - 2),
NULL); }
$ CloseHandle (proc) asserted;
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
#define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) )
IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module /*= GetModuleHandle (NULL)*/)
{
$4 assert (module);
$ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0);
$ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew);
$ return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE &&
ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL;
}
//-----------------------------------------------------------------------------------------------------------------
// TXLib continues to hack the reality to make your life better, sweeter and easier
uintptr_t _txSetProcAddress (const char funcName[], uintptr_t newFunc, const char dllName[] /*= NULL*/, int useHotPatching /*= false*/,
HMODULE module /*= NULL*/, bool debug /*= false*/)
{
$4 if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (void*) newFunc, dllName, module);
$ if (_TX_ARGUMENT_FAILED (funcName)) return 0;
$ if (_TX_ARGUMENT_FAILED (newFunc)) return 0;
$ if (!module) module = GetModuleHandle (NULL);
$ if (!module) return 0;
$ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL;
$ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL;
$ if (useHotPatching && oldFunc)
{
$ const size_t jmpSz = 1 + sizeof (DWORD); // sizeof (JMP rel instruction)
$ DWORD oldRights = 0;
$ if (!VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights)) return 0;
// Overwrite oldFunc prolog with JMP trampoline to newFunc.
// Calling oldFunc from any location will lead to newFunc call anyway.
$ *(BYTE*) ((char*)(uintptr_t) oldFunc + 0) = 0xE9; // JMP rel
$ *(DWORD*) ((char*)(uintptr_t) oldFunc + 1) = ((char*)(uintptr_t) newFunc - (char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF;
$ FlushInstructionCache (GetCurrentProcess(), (void*)(uintptr_t) oldFunc, jmpSz);
$ VirtualProtect ((void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights);
$ return (uintptr_t) oldFunc;
}
// For PE structure and Import Table format, e.g. see https://books.google.ru/books?id=ifQPC86G66sC&pg=PA255
// and below through Figure 5-5, and/or http://www.brokenthorn.com/Resources/OSDevPE.html.
$ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module);
if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$ return 0; }
$ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
$ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset);
$ if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr) return 0;
$ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL;
$ char* impDll = NULL;
$ char* impName = NULL;
$ void** impPtr = NULL;
$ bool found = false;
for (; desc->Name; desc++)
{
$ impDll = RVA_ (char*, module, desc->Name);
$ if (dllName && _stricmp (impDll, dllName) != 0) continue;
$ for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk),
thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk);
thunk0 && thunk1 && thunk1->u1.Function;
thunk0++,
thunk1++)
{
impName = (char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name;
impPtr = (void**)(uintptr_t) &thunk1->u1.Function; // Should change it, so this is ptr
if (debug) txOutputDebugPrintf ("[0x%p] %s!%s\n", *impPtr, impDll, impName);
if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) ||
(impName && _stricmp (funcName, impName) == 0))
{
found = true;
break;
}
}
$ if (found) break;
}
if (debug) txOutputDebugPrintf ("_txSetProcAddress (%s, 0x%p, %s, 0x%p): %s\n\n",
funcName, (void*) newFunc, dllName, module, (found? "FOUND" : "NOT found"));
$ if (!found) return 0;
$ DWORD rights = PAGE_READWRITE;
$ if (!VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights)) return 0;
$ *(uintptr_t*) impPtr = newFunc;
$ VirtualProtect (impPtr, sizeof (*impPtr), rights, &rights);
$ return (uintptr_t) oldFunc;
}
#undef RVA_
//-----------------------------------------------------------------------------------------------------------------
bool _txInDll()
{
$4 MODULEENTRY32 mod = { sizeof (mod) };
$ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
$ assert (sshot); if (!sshot) return false;
$ bool inDll = false;
$ for (bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod))
{
$ if (!mod.modBaseAddr) continue;
$ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr);
$ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0);
$ if (In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize))
{$ break; }
}
$ CloseHandle (sshot);
$ return inDll;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txIsConsoleSubsystem()
{
$4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders();
$ return ntHdr &&
ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC &&
(ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI);
}
//-----------------------------------------------------------------------------------------------------------------
bool _txIsBadReadPtr (const void* address)
{
MEMORY_BASIC_INFORMATION mbi = {};
if (!VirtualQuery (address, &mbi, sizeof (mbi))) return true;
if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS)) return true; // Guard page -> bad ptr
DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
return !(mbi.Protect & readRights);
}
//}
//-----------------------------------------------------------------------------------------------------------------
//! @}
//}
//=================================================================================================================
//=================================================================================================================
//{ Internal TXLib window functions (_txCanvas...)
//! @name Внутренние функции окна TXLib (_txCanvas...)
//=================================================================================================================
unsigned WINAPI _txCanvas_ThreadProc (void* data)
{
#define SetClassLong_ SetClassLongPtr
#define GCL_HICON_ GCLP_HICON
#define GCL_HICONSM_ GCLP_HICONSM
#define GCL_HCURSOR_ GCLP_HCURSOR
$8 _txCanvas_ThreadId = GetCurrentThreadId();
$ if (_TX_ARGUMENT_FAILED (data)) return false;
$ unsigned long stackSize = _TX_STACKSIZE;
$ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize));
$ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data);
$ if (!txWindow()) return TX_DEBUG_ERROR ("\a" "Cannot create canvas!"), 0;
$ HICON icon32 = LoadIcon (NULL, "_TX_ICON");
$ HICON icon16 = LoadIcon (NULL, "_TX_ICONSM");
$ HCURSOR cursor = LoadCursor (NULL, "_TX_CURSOR");
$ HMENU menu = LoadMenu (NULL, "_TX_MENU");
$ HACCEL accel = LoadAccelerators (NULL, "_TX_ACCELERATORS");
$ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32)));
$ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16)));
$ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW)));
if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); }
$ Win32::GdiSetBatchLimit (1);
_TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STARTED: " _TX_MODULE "\n"));
$ SetForegroundWindow (wnd);
$ ShowWindow (wnd, SW_SHOW);
$ UpdateWindow (wnd);
$ _txRunning = true;
$ MSG msg = {};
$ while (GetMessage (&msg, NULL, 0, 0))
{
if (!msg.hwnd) {$ continue; }
if (accel && TranslateAccelerator (wnd, accel, &msg)) {$ continue; }
$ TranslateMessage (&msg);
$ DispatchMessage (&msg);
$ Sleep (0);
}
$ if (icon16) DestroyIcon (icon16); // If Explorer is displaying Tray Notification, these
$ if (icon32) DestroyIcon (icon32); // calls will possibly fail, and we'll get resource leak.
$ LeaveCriticalSection (&_txCanvas_LockBackBuf);
_TX_ON_DEBUG (OutputDebugString (_TX_VERSION " - STOPPED: " _TX_MODULE "\n"));
$ if (_txWatchdogTimeout >= 0)
{$ Win32::_beginthread (_txWatchdogTerminator, 0, &_txWatchdogTimeout); }
$ if (_txRunning && _txMain) // Main window is destroyed but main() is still running.
{ // No chances for good termination, so use exit().
$ _txCleanup();
$ ::exit ((int) msg.wParam);
}
$ _txCanvas_ThreadId = 0;
$ return true;
#undef SetClassLong
#undef GCL_HICON_
#undef GCL_HICONSM_
#undef GCL_HCURSOR_
}
//-----------------------------------------------------------------------------------------------------------------
HWND _txCanvas_CreateWindow (const SIZE* sizePtr)
{
$8 if (_TX_ARGUMENT_FAILED (sizePtr)) return NULL;
$ bool centered = false;
if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered = true; }
$ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) };
$ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect, _txWindowStyle, false);
$ SIZE size = { rect.right - rect.left, rect.bottom - rect.top };
$ const char* wndClass = txRegisterClass ("MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0);
$ if (!wndClass) return (HWND) NULL;
$ HWND wnd = CreateWindowEx (WS_EX_APPWINDOW, wndClass, txGetModuleFileName (false), _txWindowStyle | WS_CLIPCHILDREN,
centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT,
centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT,
size.cx, size.cy, NULL, NULL, NULL, NULL);
$ if (!wnd || !txWindow())
{$ return TX_DEBUG_ERROR ("Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; }
$ HMENU menu = GetSystemMenu (txWindow(), false);
if (!menu) {$ return txWindow(); }
$ AppendMenu (menu, MF_SEPARATOR, 0, NULL) asserted;
$ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE, "Show &Console") asserted;
$ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...") asserted;
$ return txWindow();
}
//-----------------------------------------------------------------------------------------------------------------
const char* txRegisterClass (const char classId[], WNDPROC wndProc, unsigned style, int backBrush, int wndExtra)
{
$8 assert (classId);
$ assert (wndProc);
$ static char name[_TX_BUFSIZE] = "";
$ _tx_snprintf_s (name, sizeof (name) - 1, "/*---[TXLib]-[%s]------------ "
_TX_VERSION " " __FILE__ " WndClass %08lX "
"-------------[%s]-[TXLib]---*/",
classId, (unsigned long) GetTickCount(), classId);
$ WNDCLASS wc = { sizeof (wc) };
$ wc.lpszClassName = name;
$ wc.lpfnWndProc = wndProc;
$ wc.style = style;
$ wc.cbWndExtra = (wndExtra + 1) * (int) sizeof (long);
$ wc.hCursor = LoadCursor (NULL, IDC_ARROW);
$ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush);
$ ATOM atom = RegisterClass (&wc);
if (!atom) {$ TX_DEBUG_ERROR ("RegisterClass (\"%s\") failed", name); return 0; }
$ return (const char*)(uintptr_t) atom;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool _txCanvas_OK()
{
return _txCanvas_ThreadId &&
_txCanvas_Window &&
_txCanvas_BackBuf[0] &&
_txCanvas_BackBuf[1] &&
_txCanvas_Pixels;
}
//-----------------------------------------------------------------------------------------------------------------
int _txCanvas_SetRefreshLock (int count)
{
$8 int oldCount = _txCanvas_RefreshLock;
$ _txCanvas_RefreshLock = count;
$ HWND wnd = txWindow();
$ if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd)
{$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
$ return oldCount;
}
//-----------------------------------------------------------------------------------------------------------------
HICON _txCreateTXIcon (int size)
{
$8 if (_TX_ARGUMENT_FAILED (size == 32 || size == 16)) return NULL;
$ const unsigned char image32 [32*32+1] =
"00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0"
"0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0"
"0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0"
"0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0"
"0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0"
"0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0"
"0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0"
"0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000";
$ const unsigned char image16 [16*16+1] =
"0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990"
"0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000";
$ const COLORREF pal['F'-'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0,
0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff };
$ const unsigned char* image = (size == 32)? image32 : image16;
$ POINT sz = { size, size };
$ HDC dcMask = _txBuffer_Create (txWindow(), &sz); assert (dcMask);
$ HDC dcColor = _txBuffer_Create (txWindow(), &sz); assert (dcColor);
$ for (int i = 0; i < size*size; i++)
{
assert (In (std::nomeow, image[i], '0', '9') ||
In (std::nomeow, image[i], 'A', 'F'));
Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] - '0']);
}
$ ICONINFO info = { true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP),
(HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) };
$ HICON icon = CreateIconIndirect (&info);
$ assert (icon);
$ _txBuffer_Delete (&dcMask) asserted;
$ _txBuffer_Delete (&dcColor) asserted;
$ return icon;
}
//}
//=================================================================================================================
//=================================================================================================================
//{ Main window event handlers (_txCanvas_On...)
//! @name События основного окна (_txCanvas_On...)
//=================================================================================================================
//! @{
LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
{
#if defined (_TX_ALLOW_TRACE)
int inTX = _txLoc::Cur.inTX++;
if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__, __TX_FUNCTION__, "%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)",
2 * (_txLoc::Cur.inTX - 1), "", wnd, msg, wpar, lpar);
_txLoc::Cur.inTX = inTX;
#endif
$8 if (msg == WM_KEYDOWN && wpar == VK_F12 &&
GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU))
{
$ _txCanvas_OnCmdABOUT (wnd, wpar);
$ return DefWindowProc (wnd, msg, wpar, lpar);
}
WNDPROC altWndProc = _txAltWndProc; // Cache to prevent change from main thread
if (altWndProc)
{
$ LRESULT res = altWndProc (wnd, msg, wpar, lpar);
$ if (res) return res;
}
static bool bkErased = false;
switch (msg)
{
case WM_CREATE: {$ _txCanvas_OnCREATE (wnd); return 0; }
case WM_CLOSE: {$ if (_txCanvas_OnCLOSE (wnd)) break; else return 0; }
case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd); return 0; }
case WM_ERASEBKGND: {$ if (!bkErased) { bkErased = true; break; } else return 1; }
case WM_SIZE: {$ bkErased = false; break; }
case WM_PAINT: {$ _txCanvas_OnPAINT (wnd); return 0; }
case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar); return 0; }
case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar); return 0; }
case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar); return 0; }
case WM_LBUTTONUP:
case WM_LBUTTONDOWN:
case WM_RBUTTONUP:
case WM_RBUTTONDOWN:
case WM_MBUTTONUP:
case WM_MBUTTONDOWN:
case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar); return 0; }
case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd); return 0; }
case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar); return 0; }
case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar); return 0; }
default: break;
}
if (msg == WM_SYSCOMMAND) switch (wpar)
{
case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar); return 0; }
case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar); return 0; }
default: break;
}
$ return DefWindowProc (wnd, msg, wpar, lpar);
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCREATE (HWND wnd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels); assert (_txCanvas_BackBuf[0]);
$ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL); assert (_txCanvas_BackBuf[1]);
$ if (!SetTimer (wnd, _txCanvas_RefreshTimer, _txWindowUpdateInterval, NULL)) _txCanvas_RefreshTimer = 0;
$ assert (_txCanvas_RefreshTimer);
$ _txCanvas_UserDCs = new ::std::vector <HDC>;
$ _txCanvas_Window = wnd;
$ txSetDefaults();
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnDESTROY (HWND wnd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
// Инициируем остановку цикла сообщений
$ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS);
$ if (!_txCanvas_Window) return false;
// Indicate that we are about to manually terminate
$ _txExit = true;
// Lock GDI resources
$ bool locked = false;
$ _txWaitFor ((locked = txLock (false), locked), _TX_TIMEOUT);
$ if (!locked) TX_DEBUG_ERROR ("Cannot lock GDI to free resources");
// Освобождаем пользовательские ресурсы
$ if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty())
{
$ txNotifyIcon (NIIF_ERROR, NULL, "Вы забыли освободить %d HDC.", (int) _txCanvas_UserDCs->size());
$ Sleep (_TX_TIMEOUT);
$ for (size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i));
$ _txCanvas_UserDCs->clear();
}
$ delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL;
// Освобождаем ресурсы, связанные с окном
$ if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer) asserted;
$ if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1]) asserted;
$ if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0]) asserted;
$ _txCanvas_Pixels = NULL;
$ txUnlock();
// Indicate that we are destroyed
$ _txCanvas_Window = NULL;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCLOSE (HWND wnd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ if (!_txCanvas_OK()) return false;
$ if (_txMain && _txRunning &&
txMessageBox ("Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n"
"Лучше подождать, когда main() завершится - это отображается в заголовке окна.",
txGetModuleFileName (false), MB_YESNOCANCEL | MB_ICONSTOP) != IDYES) return false;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnTIMER (HWND wnd, WPARAM)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ if (_txCanvas_RefreshLock > 0 || !_txRunning) return false;
$ InvalidateRect (wnd, NULL, false) asserted;
$ UpdateWindow (wnd) asserted;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnPAINT (HWND wnd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ if (!_txCanvas_OK()) return false;
$ bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) &&
GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT);
$ PAINTSTRUCT ps = {};
$ HDC wndDc = BeginPaint (wnd, &ps);
$ if (!wndDc) return false;
$ HDC dc0 = _txCanvas_BackBuf[0],
dc1 = _txCanvas_BackBuf[1];
$ RECT r = {};
$ GetClientRect (wnd, &r) asserted;
$ POINT wndSize = { r.right - r.left, r.bottom - r.top };
$ POINT dcSize = txGetExtent (dc1);
$ if ((_txCanvas_RefreshLock <= 0 || forceRedraw) &&
txLock (false))
{
$ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY);
$ _txConsole_Draw (dc1);
$ txUnlock();
}
// Magic 100500 value is used to completely block screen refresh.
// Since no value can be 100500 or above, this condition is always true and the refresh cannot be blocked IRL.
// Do not use 100501 because it may lead to errors on some compilers and possible may crash the compilers
// themselves.
// Yes guys, with all your software installed. :(
$ if (_txCanvas_RefreshLock != 100500)
{
if (_txSwapBuffers)
{
$ _txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY);
}
else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y)
{
$ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY);
}
else
{
$ Win32::SetStretchBltMode (wndDc, HALFTONE);
$ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY);
}
}
$ EndPaint (wnd, &ps) asserted;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info)
{
$8 INPUT_RECORD evt = {};
$ evt.EventType = KEY_EVENT;
$ evt.Event.KeyEvent.bKeyDown = true;
$ evt.Event.KeyEvent.wRepeatCount = 1;
$ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2); // 2 == MAPVK_VK_TO_CHAR
$ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16);
$ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk;
$ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0;
$ if (evt.Event.KeyEvent.uChar.AsciiChar) return false; // Let TranslateMessage() and WM_CHAR do the job
$ DWORD written = 0;
$ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written);
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info)
{
$8 INPUT_RECORD evt = {};
$ evt.EventType = KEY_EVENT;
$ evt.Event.KeyEvent.bKeyDown = true;
$ evt.Event.KeyEvent.wRepeatCount = 1;
$ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch);
$ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16);
$ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3); // 3 == MAPVK_VSC_TO_VK_EX
$ evt.Event.KeyEvent.dwControlKeyState = 0;
$ DWORD written = 0;
$ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written);
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ if (!_txCanvas_OK()) return false;
$ if (_txMousePos.x == -1 && _txMousePos.y == -1)
{
$ TRACKMOUSEEVENT track = { sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT };
$ TrackMouseEvent (&track);
}
$ _txMousePos.x = LOWORD (coords);
$ _txMousePos.y = HIWORD (coords);
$ _txMouseButtons = (unsigned) buttons;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnMOUSELEAVE (HWND)
{
$8 _txMousePos.x = -1;
$ _txMousePos.y = -1;
$ _txMouseButtons = 0;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar)
{
$8 if (_TX_ARGUMENT_FAILED (lpar)) return false;
$ const CREATESTRUCT* create = (CREATESTRUCT*) lpar;
$ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style,
create->x, create->y, create->cx, create->cy,
create->hwndParent, create->hMenu, NULL, create->lpCreateParams);
$ *(HWND*) create->hInstance = wnd;
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar)
{
$8 if (_TX_ARGUMENT_FAILED (lpar)) return false;
$ DestroyWindow ((HWND) lpar);
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd)
{
$8 if (_TX_ARGUMENT_FAILED (wnd)) return false;
$ HWND console = Win32::GetConsoleWindow();
$ if (!console) return false;
$ bool visible = !!IsWindowVisible (console);
$ ShowWindow (console, visible? SW_HIDE : SW_SHOW);
$ visible = !!IsWindowVisible (console);
$ CheckMenuItem (GetSystemMenu (wnd, false), (int) cmd, visible? MF_CHECKED : MF_UNCHECKED);
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
bool _txCanvas_OnCmdABOUT (HWND, WPARAM)
{
$8 //{ Overriding the missing names, if the set is uncomplete
#if defined (__MODULE)
#define ABOUT_NAME_ __MODULE
#else
#define ABOUT_NAME_ "TXLib"
#endif
#if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR)
#ifndef __MODULE
#define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n"
#endif
#ifndef __VERSION
#define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n"
#endif
#ifndef __DESCRIPTION
#define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n"
#endif
#ifndef __AUTHOR
#define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name."
#endif
#endif
//}
$ static char text[_TX_BUFSIZE] = "";
$ _tx_snprintf_s (text, sizeof (text) - 1,
"Application:\n\n"
#if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR)
__MODULE " version " __VERSION "\n" __DESCRIPTION "\n" "Copyright (c) " __AUTHOR "\n"
#else
"Здесь могла бы быть Ваша реклама :)\n"
"#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n"
#endif
"\n" "%s", _txAppInfo());
$ txMessageBox (text, "About " ABOUT_NAME_, MB_ICONINFORMATION);
// And a bit of HTTP-code in C++ function:
goto http;
http://sizeof.livejournal.com
$ return true;
#undef ABOUT_NAME_
}
//! @}
//}
//=================================================================================================================
//=================================================================================================================
//{ Console-support functions (_txConsole...)
//! @name Функции консольного окна (_txConsole...)
//=================================================================================================================
//! @{
HWND _txConsole_Attach()
{
$1 HWND console = Win32::GetConsoleWindow();
$ if (!console)
{
$ FreeConsole();
$ AllocConsole();
}
$ console = Win32::GetConsoleWindow();
$ if (!console) return NULL;
$ txSetLocale(); // Устанавливаем русскую кодовую страницу для консоли Windows
$ static bool done = false;
$ if (done) return console;
$ _txConsole_SetUnicodeFont(); // Впечатлительным лучше сюда не смотреть.
$ if (!_txIsConsoleSubsystem())
{$ txReopenStdio(); } // Переоткрываем потоки ввода-вывода, если subsystem != console
// That's all, folks
$ done = true;
$ return console;
}
//-----------------------------------------------------------------------------------------------------------------
int txSetLocale (int codepage /*= _TX_CODEPAGE*/,
const char locale[] /*= _TX_LOCALE*/, const wchar_t wLocale[] /*= _TX_WLOCALE*/)
{
$1 int oldPage = GetConsoleOutputCP();
// Устанавливаем нужную кодовую страницу для консоли Windows
$ if (codepage)
{
$ SetConsoleCP (codepage);
$ SetConsoleOutputCP (codepage);
}
// Устанавливаем нужную кодовую страницу для стандартной библиотеки, иначе не будут работать Unicode-версии
// функций (wprintf, ...). Если компилите с помощью gcc и собираетесь использовать L"unicode-строки" с определенным
// языком, укажите опции в командной строке компилятора g++: -finput-charset=NNNN -fexec-charset=NNNN, где NNNN -
// обозначение кодовой страницы (например, для русского языка - CP1251).
$ if (locale)
{
$ setlocale (LC_ALL, locale);
$ setlocale (LC_NUMERIC, "C"); // Return to decimal point (3.14) instead of comma (3,14) in floating numbers
}
#ifndef __CYGWIN__
$ const bool wine = !!Win32::wine_get_version; // Linux::Wine v1.2.2+ compatibility.
$ if (wLocale && !wine)
{
$ _wsetlocale (LC_ALL, wLocale);
$ _wsetlocale (LC_NUMERIC, L"C"); // L"C" (see above)
}
#endif
(void) wLocale;
$ return oldPage;
}
//-----------------------------------------------------------------------------------------------------------------
void txReopenStdio()
{
$1 // Переоткрываем заново <s>Америку
8029 int txRegQuery (
const char* keyName,
const char* valueName,
void* value,
size_t szValue)
8031 if (_TX_ARGUMENT_FAILED (keyName))
return 0;
8035 #define EQU_(name1, name2) ( _strnicmp (keyName, name1 "\\", sizeof (name1)) == 0 || \
8036 _strnicmp (keyName, name2 "\\", sizeof (name2)) == 0 )
8038 if (EQU_(
"HKLM",
"HKEY_LOCAL_MACHINE")) hive = HKEY_LOCAL_MACHINE;
8039 else if (EQU_(
"HKCU",
"HKEY_CURRENT_USER")) hive = HKEY_CURRENT_USER;
8040 else if (EQU_(
"HKCR",
"HKEY_CLASSES_ROOT")) hive = HKEY_CLASSES_ROOT;
8041 else if (EQU_(
"HKU",
"HKEY_USERS")) hive = HKEY_USERS;
8042 else if (EQU_(
"HKCC",
"HKEY_CURRENT_CONFIG")) hive = HKEY_CURRENT_CONFIG;
8044 else { _TX_ARGUMENT_FAILED ((
"keyName должно начинаться с HKLM\\, HKCU\\, HKCR\\, HKU\\ или HKCC\\ ", hive));
return 0; }
8048 keyName = strchr (keyName,
'\\') + 1;
8053 bool ok = !!RegOpenKeyEx (hive, keyName, 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS;
8054 if (ok) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, NULL, &size) == ERROR_SUCCESS;
8055 if (ok && value && size < szValue) ok &= !!RegQueryValueEx (key, valueName, NULL, NULL, (BYTE*) value, &size) == ERROR_SUCCESS;
8056 if (key) ok &= !!RegCloseKey (key);
8061 #if defined (_MSC_VER) && (_MSC_VER == 1800)
8062 #pragma warning (pop)
8070 $1
if (!_txInitialized) _txInitialized = _txInitialize();
8074 $ SetLastErrorEx (ERROR_INVALID_DATA, 0);
8075 $ _TX_ON_DEBUG (
TX_ERROR (
"\a" "Окно рисования уже создано!"));
8081 $ _txMain = ! FindAtom (
"_txMain");
8082 $ (void) AddAtom (
"_txMain");
8087 $ _txRunning =
false;
8091 $
static SIZE size = {
ROUND (sizeX),
ROUND (sizeY) };
8092 $
if (centered) { size.cx *= -1; size.cy *= -1; }
8097 $ _txCanvas_Thread = (HANDLE) Win32::_beginthreadex (NULL, 0, _txCanvas_ThreadProc, &size, 0, &
id);
8099 $
if (!_txCanvas_Thread)
return TX_DEBUG_ERROR (
"\a" "Cannot start canvas thread."), (HWND) NULL;
8103 $
if (!_txRunning)
return TX_DEBUG_ERROR (
"\a" "Cannot create canvas window."), (HWND) NULL;
8106 $ HWND console = Win32::GetConsoleWindow();
8109 $ GetWindowThreadProcessId (console, &proc);
8111 $
if (console && (proc == GetCurrentProcessId() || _txIsParentWaitable()))
8114 $ HMENU menu = GetSystemMenu (
txWindow(),
false);
8115 if (menu) {$ CheckMenuItem (menu, _TX_IDM_CONSOLE, (console? (IsWindowVisible (console)? MF_CHECKED : 0) : MF_DISABLED)); }
8117 $ Win32::GdiSetBatchLimit (1);
8123 #if !defined (__CYGWIN__)
8132 HWND txCreateExtraWindow (CREATESTRUCT createData)
8134 $1
if (_TX_TXWINDOW_FAILED())
return NULL;
8136 $
volatile HWND wnd = NULL;
8137 $ createData.hInstance = (HINSTANCE)(uintptr_t) &wnd;
8139 $ PostMessage (
txWindow(), _TX_WM_CREATEWND, 0, (LPARAM) &createData)
asserted;
8154 $ GetClientRect (Win32::GetConsoleWindow(), &r)
asserted;
8155 $ SIZE szCon = { r.right - r.left, r.bottom - r.top };
8157 $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
8159 $ CONSOLE_SCREEN_BUFFER_INFO con = {{80, 25}, {}, 0, {0, 0, 80-1, 25-1}, {80, 25}};
8160 $ GetConsoleScreenBufferInfo (out, &con);
8162 $ SIZE szTxt = { (short) (con.srWindow.Right - con.srWindow.Left + 1),
8163 (short) (con.srWindow.Bottom - con.srWindow.Top + 1) };
8167 $ _txBuffer_Select (Win32::GetStockObject (WHITE_PEN), dc)
asserted;
8168 $ _txBuffer_Select (Win32::GetStockObject (WHITE_BRUSH), dc)
asserted;
8170 $ _txBuffer_Select (Win32::CreateFont (szCon.cy/szTxt.cy, szCon.cx/szTxt.cx,
8171 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
8172 RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
8177 $ Win32::SetBkMode (dc, TRANSPARENT)
asserted;
8179 $ Win32::SetROP2 (dc, R2_COPYPEN)
asserted;
8180 $ Win32::SetStretchBltMode (dc, HALFTONE)
asserted;
8192 Win32::CreateFont (szCanvas.y/szTxt.cy, szCanvas.x/szTxt.cx,
8193 0, 0, FW_REGULAR, FALSE, FALSE, FALSE,
8194 RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
8197 Win32::GetStockObject (SYSTEM_FIXED_FONT);
8199 $ _txBuffer_Select (font, _txCanvas_BackBuf[1]);
8204 $
if (con.dwCursorPosition.X) _putch (
'\n');
8206 $
short delta = (short) (con.dwCursorPosition.Y - con.srWindow.Top);
8208 $ con.srWindow.Top = (short) (con.srWindow.Top + delta);
8209 $ con.srWindow.Bottom = (short) (con.srWindow.Bottom + delta);
8211 $ SMALL_RECT src = { 0, 0, (short) (con.dwSize.X - 1), (short) (con.dwSize.Y - 1) };
8212 $ CHAR_INFO fill = { {
' '}, FOREGROUND_LIGHTGRAY };
8213 $ COORD dest = { 0, (short) -delta };
8215 $ con.dwCursorPosition.X = 0;
8216 $ con.dwCursorPosition.Y = (short) (con.dwCursorPosition.Y - delta);
8218 $ (con.srWindow.Bottom < con.dwSize.Y &&
8219 SetConsoleWindowInfo (out,
true, &con.srWindow))
8221 (ScrollConsoleScreenBuffer (out, &src, NULL, dest, &fill),
8222 SetConsoleCursorPosition (out, con.dwCursorPosition));
8234 return (_txCanaryFirst == 0x776F656D &&
8235 _txCanaryLast == 0x5E2E2E5E &&
8238 #
if defined (_MSC_VER)
8239 && _CrtCheckMemory()
8254 OutputDebugString (
"\n");
8260 _TX_CALLv (Win32::_cexit, ());
8265 void _txOnExit (
int retcode)
8269 OutputDebugString (
"\n");
8278 Win32::exit (retcode);
8283 void _txOnExitProcess (
unsigned retcode)
8287 OutputDebugString (
"\n");
8296 Win32::ExitProcess (retcode);
8301 bool _txOnTerminateProcess (HANDLE process,
unsigned retcode)
8305 OutputDebugString (
"\n");
8314 return Win32::TerminateProcess (process, retcode);
8319 void _txOnFatalExit (
int retcode)
8321 OutputDebugString (
"\n");
8327 _TX_CALLv (Win32::FatalExit, (retcode));
8330 Win32::TerminateProcess (GetCurrentProcess(), retcode);
8335 void _txOnFatalAppExitA (
unsigned action,
const char message[])
8337 OutputDebugString (
"\n");
8343 _TX_CALLv (Win32::FatalAppExitA, (action, message));
8346 Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
8351 BOOL WINAPI _txOnConsoleCtrlEvent (DWORD type)
8353 OutputDebugString (
"\n");
8358 case CTRL_LOGOFF_EVENT:
8359 case CTRL_SHUTDOWN_EVENT: $ _txExit =
true;
8362 case CTRL_CLOSE_EVENT:
8363 case CTRL_BREAK_EVENT:
8375 if (!_txInitialized)
return;
8376 else _txInitialized =
false;
8378 $3 _txRunning =
false;
8379 $ _txConsole_IsBlinking =
false;
8382 $ HWND console = Win32::GetConsoleWindow();
8383 $
unsigned thread = GetCurrentThreadId();
8385 $ HWND wnd = (canvas)? canvas : console;
8387 $
bool externTerm = (thread != _txMainThreadId &&
8388 thread != _txCanvas_ThreadId);
8390 $
int isParentWaitable = _txIsParentWaitable (&parent);
8391 $
bool waitableParent = !externTerm && isParentWaitable;
8399 $
if (console) EnableWindow (console,
true);
8402 $
if (_txMain && !externTerm && wnd != NULL)
8403 {$ _txSetFinishedText (wnd); }
8407 $
bool paused =
false;
8408 $
if (((canvas? _txMain : _txConsole) && !_txExit) || (_txErrors && thread == _txMainThreadId))
8412 if (isParentWaitable >= 0) {$ SetWindowPos (wnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); }
8413 $ EnableWindow (wnd,
true);
8416 $
if (console && isParentWaitable >= 0)
8418 $ _txPauseBeforeTermination (canvas);
8424 {$ SendNotifyMessage (
txWindow(), WM_DESTROY, 0, 0); }
8431 $
if (GetCurrentThreadId() != _txMainThreadId)
8432 {$ SuspendThread (_txMainThread); }
8433 $
if (GetCurrentThreadId() != _txCanvas_ThreadId)
8434 {$ SuspendThread (_txCanvas_Thread); }
8436 $
if (_txMainThread)
8437 {$ CloseHandle (_txMainThread)
asserted; _txMainThread = NULL; }
8438 $
if (_txCanvas_Thread)
8439 {$ CloseHandle (_txCanvas_Thread)
asserted; _txCanvas_Thread = NULL; }
8442 {$ DeleteCriticalSection (&_txCanvas_LockBackBuf); CRITICAL_SECTION zero = {0, -1}; _txCanvas_LockBackBuf = zero; }
8444 $ console = Win32::GetConsoleWindow();
8446 $
if (_txMain && _txConsole)
8447 {$ _txConsole_Detach (waitableParent && !externTerm); }
8449 $
bool parentKilled =
false;
8452 $ parentKilled = _txKillProcess (parent);
8455 $
if (!parentKilled || _txIsParentWaitable (&parent))
8456 {$ PostMessage (console, WM_CHAR,
'\n', 0); }
8459 $ std::cout.flush();
8460 $ std::cerr.flush();
8461 $ std::clog.flush();
8464 $ _txSymGetFromAddr (NULL);
8466 _TX_ON_DEBUG (OutputDebugString (
"\n");
8468 OutputDebugString (
"\n"));
8476 int _txSetFinishedText (HWND wnd)
8480 static LRESULT getWindowText (HWND window,
wchar_t text[],
size_t size)
8482 $3 memset (text, 0, size *
sizeof (*text));
8484 $
return SendMessageTimeoutW (window, WM_GETTEXT, (WPARAM) size, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG,
_TX_TIMEOUT, NULL);
8487 static LRESULT setWindowText (HWND window,
wchar_t text[])
8489 $1
return SendMessageTimeoutW (window, WM_SETTEXT, 0, (LPARAM) text, SMTO_BLOCK | SMTO_ABORTIFHUNG,
_TX_TIMEOUT, NULL);
8493 $1
static wchar_t title [
_TX_BUFSIZE+15] = L
"TXLib";
8495 $ tools::getWindowText (wnd, title,
_TX_BUFSIZE-1);
8500 $ tools::setWindowText (wnd, title);
8501 $ tools::getWindowText (wnd, title,
_TX_BUFSIZE-1);
8502 $
if (len <= (
int)
_TX_BUFSIZE-1-2 && title [len+2] == (
wchar_t) 0x0417 )
return 0;
8506 $ tools::setWindowText (wnd, title);
8507 $ tools::getWindowText (wnd, title,
_TX_BUFSIZE-1);
8508 $
if (len <= (
int)
_TX_BUFSIZE-1-2 && title [len+2] == (
wchar_t) 0x0046)
return 1;
8515 void _txPauseBeforeTermination (HWND canvas)
8517 $3
while (_kbhit()) (void)_getch();
8519 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
8520 $
bool kbRedir = !GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
8521 $
bool kbWait = (_txGetInput() == EOF);
8522 $
bool wine = !!Win32::wine_get_version;
8524 $
if (kbWait && !canvas && !kbRedir && !wine)
8528 $ fprintf (stderr, (!_txErrors?
"\n" "[Нажмите любую клавишу для завершения]" :
8529 "\n" "[Press F to Pay Respects...]"));
8533 $
for (
int i = 1; ; i++)
8537 if (!kbWait || (kbRedir && !canvas)) {$
break; }
8539 if (!wine && _txGetInput() != EOF) {$
break; }
8541 if (canvas && !_txCanvas_ThreadId) {$
break; }
8543 if (!Win32::GetConsoleWindow()) {$
break; }
8545 if (_TX_CALL (Win32::GhostWindowFromHungWindow, (canvas)))
8546 {$
TX_ERROR (
"Программа зависла и будет завершена.");
break; }
8548 if (canvas && _TX_CALL (Win32::IsHungAppWindow, (canvas)))
8549 {$ _txTrace (__FILE__, __LINE__, NULL,
"WARNING: Программа-таки зависла и будет завершена.");
break; }
8551 if (canvas && !SendMessageTimeout (canvas, WM_NULL, 0,0, SMTO_BLOCK | SMTO_ABORTIFHUNG,
_TX_TIMEOUT, NULL))
8552 {$ _txTrace (__FILE__, __LINE__, NULL,
"WARNING: Программа не отвечает и будет завершена.");
break; }
8554 if (!wine && !(i % 100500))
8555 {$ fprintf (stderr,
"\r" "[Так нажмите же какую-нибудь клавишу для моего завершения]"); }
8558 $
while (!wine && _kbhit()) (void)_getch();
8560 $ fprintf (stderr,
"\n");
8567 $4 HANDLE con = GetStdHandle (STD_INPUT_HANDLE);
8570 $
if (GetConsoleMode (con, &nChars) == 0 &&
8571 PeekNamedPipe (con, NULL, 0, NULL, &nChars, NULL))
8573 $
return (nChars)? fgetc (stdin) : EOF;
8581 #if defined (_MSC_VER) && (_MSC_VER < 1700)
8583 $
if (fseek (stdin, 1, SEEK_CUR) != EOF)
8585 $ (void) fseek (stdin, -1, SEEK_CUR);
8586 $
return fgetc (stdin);
8596 int _txIsParentWaitable (DWORD* parentPID )
8598 $4 PROCESSENTRY32* info = _txFindProcess();
8599 $
if (!info)
return 0;
8601 $ info = _txFindProcess (info->th32ParentProcessID);
8602 $
if (!info)
return 0;
8604 $
char parent [MAX_PATH] =
"";
8605 $
strncpy_s (parent,
sizeof (parent), info->szExeFile, sizeof (parent) - 1);
8606 $
if (parentPID) *parentPID = info->th32ProcessID;
8608 $ info = _txFindProcess (info->th32ParentProcessID);
8613 $
for (
char* p =
strtok_s (list,
", ", &ctx); p; p =
strtok_s (NULL,
", ", &ctx))
8615 $
char* gp = strchr (p,
':');
8621 $
if (_stricmp (p, parent) != 0) {
continue; }
8623 $
if (info)
if (_stricmp (gp, info->szExeFile) == 0)
8624 {$
return islower ((
unsigned char) *gp)? +1 : -1; }
8628 $
if (_stricmp (p, parent) == 0)
8629 {$
return islower ((
unsigned char) *p)? +1 : -1; }
8638 void _txWatchdogTerminator (
void* timeout)
8640 $3
if (_TX_ARGUMENT_FAILED (timeout))
return;
8642 $ Sleep (*(
int*) timeout);
8644 $ OutputDebugString (
"\n");
8646 _TX_VERSION, __func__, *(
int*) timeout, ((__func__[8] ==
'd')?
"Bark, bark" :
"Meow, meow"));
8648 $
if (_txIsParentWaitable (&parent))
8653 $ _txKillProcess (parent);
8655 $ PostMessage (GetConsoleWindow(), WM_CHAR,
'\n', 0);
8659 $ Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
8669 PROCESSENTRY32* _txFindProcess (
unsigned pid )
8671 $4
static PROCESSENTRY32 info = {
sizeof (info) };
8672 $
if (!pid)
return &info;
8674 $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
8675 $
assert (sshot);
if (!sshot)
return NULL;
8677 $
for (
bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info))
8678 if (info.th32ProcessID == pid)
break;
8680 $ CloseHandle (sshot);
8689 bool _txKillProcess (DWORD pid)
8691 $3
if (_TX_ARGUMENT_FAILED (pid))
return false;
8693 $ HANDLE token = INVALID_HANDLE_VALUE;
8694 $ OpenProcessToken (GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &token)
asserted;
8697 $ LookupPrivilegeValue (NULL, SE_DEBUG_NAME, &luid)
asserted;
8699 $ TOKEN_PRIVILEGES priv = { 1, {{{ luid.LowPart, luid.HighPart}, SE_PRIVILEGE_ENABLED }}};
8700 $ TOKEN_PRIVILEGES old = {};
8703 $ AdjustTokenPrivileges (token,
false, &priv,
sizeof (priv), &old, &oldSz)
asserted;
8705 $ HANDLE proc = OpenProcess (PROCESS_ALL_ACCESS, 0, pid);
8706 $
if (!proc)
return false;
8708 $
bool ok = !!Win32::TerminateProcess (proc, 0);
8709 $ CloseHandle (proc);
8716 int txTaskKill (
const char i[] ,
8717 const char doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine[] ,
8722 #define name i // Great name!
8723 #define cmdLineSubstr doYouWantToFindSomethingInTheCommandLineIDidSomethingForYouToFindSomethingInTheCommandLineMaybeYouWillFindSomeInterestingInTheCommandLineSoIDidSomethingForYouInTheCommandLine
8724 #define pid x // Another great name, isn't it?
8726 $3
if (_TX_ARGUMENT_FAILED ((name || cmdLineSubstr || pid) &&
"Вот такие тут интересные имена встречаются..."))
return false;
8729 if (cmdLineSubstr) {$ MultiByteToWideChar (
_TX_CODEPAGE, 0, cmdLineSubstr, -1, cmdLineSubstrW,
sizearr (cmdLineSubstrW)); }
8731 $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
8732 $
assert (sshot);
if (!sshot)
return 0;
8736 $ PROCESSENTRY32 info = {
sizeof (info) };
8737 $
for (
bool ok = !!Process32First (sshot, &info); ok; ok = !!Process32Next (sshot, &info))
8741 if (!kill && pid && info.th32ParentProcessID == pid) {$ kill =
true; }
8743 if (!kill && name && _stricmp (info.szExeFile, name) == 0) {$ kill =
true; }
8748 if (!_txGetCommandLine (cmdLineW,
sizearr (cmdLineW), info.th32ProcessID)) {
continue; }
8750 if (*cmdLineW && stristrw (cmdLineW, cmdLineSubstrW)) {$ kill =
true; }
8755 $
if (_txKillProcess (info.th32ProcessID))
8760 $ CloseHandle (sshot);
8771 bool _txGetCommandLine (
wchar_t cmdLine[],
size_t szCmdLine,
unsigned pid )
8773 $4
if (_TX_ARGUMENT_FAILED (cmdLine))
return false;
8774 $
if (_TX_ARGUMENT_FAILED (szCmdLine >= 2))
return false;
8776 $
if (pid == (
unsigned) _getpid())
8778 $
wcsncpy_s (cmdLine, szCmdLine, GetCommandLineW(), szCmdLine-1);
8782 $ HANDLE proc = OpenProcess (PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
false, pid);
8783 if (!proc) {$
return false; }
8785 $ Win32::PROCESS_BASIC_INFORMATION pbi = {};
8786 $ Win32::NtQueryInformationProcess (proc, 0 , &pbi,
sizeof (pbi), NULL) == 0
asserted;
8792 $ Win32::PEB peb = {};
8793 if (ok && pbi.PebBaseAddress) {$ ok &= !!ReadProcessMemory (proc, pbi.PebBaseAddress, &peb, sizeof (peb), NULL); }
8795 $ Win32::RTL_USER_PROCESS_PARAMETERS params = {};
8796 if (ok && peb.ProcessParameters) {$ ok &= !!ReadProcessMemory (proc, peb.ProcessParameters, ¶ms, sizeof (params), NULL); }
8799 if (ok && params.CommandLine.Buffer) {$ ok &= !!ReadProcessMemory (proc, params.CommandLine.Buffer, cmdLine,
8800 MIN (params.CommandLine.Length + 2, (
int) (szCmdLine * sizeof (*cmdLine)) - 2),
8809 #define RVA_(type, module, addr) ( (type) ((uintptr_t) (module) + (uintptr_t) (addr)) )
8811 IMAGE_NT_HEADERS* _txGetNtHeaders (HMODULE module )
8815 $ IMAGE_DOS_HEADER* dosHdr = RVA_ (IMAGE_DOS_HEADER*, module, 0);
8816 $ IMAGE_NT_HEADERS* ntHdr = RVA_ (IMAGE_NT_HEADERS*, module, dosHdr->e_lfanew);
8818 $
return (dosHdr->e_magic == IMAGE_DOS_SIGNATURE &&
8819 ntHdr->Signature == IMAGE_NT_SIGNATURE)? ntHdr : NULL;
8826 uintptr_t _txSetProcAddress (
const char funcName[], uintptr_t newFunc,
const char dllName[] ,
int useHotPatching ,
8827 HMODULE module ,
bool debug )
8829 $4
if (debug)
txOutputDebugPrintf (
"_txSetProcAddress (%s, 0x%p, %s, 0x%p):\n", funcName, (
void*) newFunc, dllName, module);
8831 $
if (_TX_ARGUMENT_FAILED (funcName))
return 0;
8832 $
if (_TX_ARGUMENT_FAILED (newFunc))
return 0;
8834 $
if (!module) module = GetModuleHandle (NULL);
8835 $
if (!module)
return 0;
8837 $ HMODULE dll = (dllName)? GetModuleHandle (dllName) : NULL;
8838 $ PROC oldFunc = (dll)? GetProcAddress (dll, funcName) : NULL;
8840 $
if (useHotPatching && oldFunc)
8842 $
const size_t jmpSz = 1 +
sizeof (DWORD);
8844 $ DWORD oldRights = 0;
8845 $
if (!VirtualProtect ((
void*)(uintptr_t) oldFunc, jmpSz, PAGE_EXECUTE_READWRITE, &oldRights))
return 0;
8850 $ *(BYTE*) ((
char*)(uintptr_t) oldFunc + 0) = 0xE9;
8851 $ *(DWORD*) ((
char*)(uintptr_t) oldFunc + 1) = ((
char*)(uintptr_t) newFunc - (
char*)(uintptr_t) oldFunc - jmpSz) & 0xFFFFFFFF;
8853 $ FlushInstructionCache (GetCurrentProcess(), (
void*)(uintptr_t) oldFunc, jmpSz);
8855 $ VirtualProtect ((
void*)(uintptr_t) oldFunc, jmpSz, oldRights, &oldRights);
8857 $
return (uintptr_t) oldFunc;
8863 $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders (module);
8864 if (!ntHdr || (ntHdr ->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC)) {$
return 0; }
8866 $ DWORD impOffset = ntHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
8867 $ IMAGE_IMPORT_DESCRIPTOR* desc = RVA_ (IMAGE_IMPORT_DESCRIPTOR*, module, impOffset);
8869 $
if (desc == (IMAGE_IMPORT_DESCRIPTOR*) ntHdr)
return 0;
8871 $ IMAGE_THUNK_DATA* thunk0 = NULL, * thunk1 = NULL;
8872 $
char* impDll = NULL;
8873 $
char* impName = NULL;
8874 $
void** impPtr = NULL;
8875 $
bool found =
false;
8877 for (; desc->Name; desc++)
8879 $ impDll = RVA_ (
char*, module, desc->Name);
8880 $
if (dllName && _stricmp (impDll, dllName) != 0)
continue;
8882 $
for (thunk0 = RVA_ (IMAGE_THUNK_DATA*, module, desc->OriginalFirstThunk),
8883 thunk1 = RVA_ (IMAGE_THUNK_DATA*, module, desc->FirstThunk);
8885 thunk0 && thunk1 && thunk1->u1.Function;
8890 impName = (
char*) RVA_ (IMAGE_IMPORT_BY_NAME*, module, thunk0->u1.AddressOfData) -> Name;
8891 impPtr = (
void**)(uintptr_t) &thunk1->u1.Function;
8895 if ((oldFunc && (uintptr_t) oldFunc == (uintptr_t) *impPtr) ||
8896 (impName && _stricmp (funcName, impName) == 0))
8907 funcName, (
void*) newFunc, dllName, module, (found?
"FOUND" :
"NOT found"));
8908 $
if (!found)
return 0;
8910 $ DWORD rights = PAGE_READWRITE;
8911 $
if (!VirtualProtect (impPtr,
sizeof (*impPtr), rights, &rights))
return 0;
8913 $ *(uintptr_t*) impPtr = newFunc;
8915 $ VirtualProtect (impPtr,
sizeof (*impPtr), rights, &rights);
8917 $
return (uintptr_t) oldFunc;
8926 $4 MODULEENTRY32 mod = {
sizeof (mod) };
8928 $ HANDLE sshot = CreateToolhelp32Snapshot (TH32CS_SNAPMODULE, 0);
8929 $
assert (sshot);
if (!sshot)
return false;
8931 $
bool inDll =
false;
8933 $
for (
bool ok = !!Module32First (sshot, &mod); ok; ok = !!Module32Next (sshot, &mod))
8935 $
if (!mod.modBaseAddr)
continue;
8937 $ IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders ((HMODULE) mod.modBaseAddr);
8939 $ inDll = ntHdr && ((ntHdr->FileHeader.Characteristics & IMAGE_FILE_DLL) != 0);
8941 $
if (
In (std::nomeow, (BYTE*)(uintptr_t)_txInDll, mod.modBaseAddr, mod.modBaseAddr + mod.modBaseSize))
8945 $ CloseHandle (sshot);
8951 bool _txIsConsoleSubsystem()
8953 $4 IMAGE_NT_HEADERS* ntHdr = _txGetNtHeaders();
8956 ntHdr ->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC &&
8958 (ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI ||
8959 ntHdr ->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_POSIX_CUI);
8964 bool _txIsBadReadPtr (
const void* address)
8966 MEMORY_BASIC_INFORMATION mbi = {};
8967 if (!VirtualQuery (address, &mbi,
sizeof (mbi)))
return true;
8969 if (mbi.Protect & (PAGE_GUARD | PAGE_NOACCESS))
return true;
8971 DWORD readRights = PAGE_READONLY | PAGE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY;
8973 return !(mbi.Protect & readRights);
8988 unsigned WINAPI _txCanvas_ThreadProc (
void* data)
8990 #define SetClassLong_ SetClassLongPtr
8991 #define GCL_HICON_ GCLP_HICON
8992 #define GCL_HICONSM_ GCLP_HICONSM
8993 #define GCL_HCURSOR_ GCLP_HCURSOR
8995 $8 _txCanvas_ThreadId = GetCurrentThreadId();
8997 $
if (_TX_ARGUMENT_FAILED (data))
return false;
9000 $ _TX_CALL (Win32::SetThreadStackGuarantee, (&stackSize));
9002 $ HWND wnd = _txCanvas_CreateWindow ((SIZE*) data);
9005 $ HICON icon32 = LoadIcon (NULL,
"_TX_ICON");
9006 $ HICON icon16 = LoadIcon (NULL,
"_TX_ICONSM");
9007 $ HCURSOR cursor = LoadCursor (NULL,
"_TX_CURSOR");
9008 $ HMENU menu = LoadMenu (NULL,
"_TX_MENU");
9009 $ HACCEL accel = LoadAccelerators (NULL,
"_TX_ACCELERATORS");
9011 $ SetClassLong_ (wnd, GCL_HICON_, (LONG_PTR) (icon32? icon32 : _txCreateTXIcon (32)));
9012 $ SetClassLong_ (wnd, GCL_HICONSM_, (LONG_PTR) (icon16? icon16 : _txCreateTXIcon (16)));
9013 $ SetClassLong_ (wnd, GCL_HCURSOR_, (LONG_PTR) (cursor? cursor : LoadCursor (NULL, IDC_ARROW)));
9015 if (menu) {$ SetMenu (wnd, menu); DrawMenuBar (wnd); }
9017 $ Win32::GdiSetBatchLimit (1);
9021 $ SetForegroundWindow (wnd);
9023 $ ShowWindow (wnd, SW_SHOW);
9024 $ UpdateWindow (wnd);
9026 $ _txRunning =
true;
9029 $
while (GetMessage (&msg, NULL, 0, 0))
9031 if (!msg.hwnd) {$
continue; }
9033 if (accel && TranslateAccelerator (wnd, accel, &msg)) {$
continue; }
9035 $ TranslateMessage (&msg);
9036 $ DispatchMessage (&msg);
9041 $
if (icon16) DestroyIcon (icon16);
9042 $
if (icon32) DestroyIcon (icon32);
9044 $ LeaveCriticalSection (&_txCanvas_LockBackBuf);
9051 $
if (_txRunning && _txMain)
9054 $ ::exit ((
int) msg.wParam);
9057 $ _txCanvas_ThreadId = 0;
9068 HWND _txCanvas_CreateWindow (
const SIZE* sizePtr)
9070 $8
if (_TX_ARGUMENT_FAILED (sizePtr))
return NULL;
9072 $
bool centered =
false;
9073 if (sizePtr->cx < 0 && sizePtr->cy < 0) {$ centered =
true; }
9075 $ SIZE screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) };
9076 $ RECT rect = { 0, 0, abs (sizePtr->cx), abs (sizePtr->cy) }; AdjustWindowRect (&rect,
_txWindowStyle,
false);
9077 $ SIZE size = { rect.right - rect.left, rect.bottom - rect.top };
9079 $
const char* wndClass = txRegisterClass (
"MAIN", _txCanvas_WndProc, CS_HREDRAW | CS_VREDRAW | CS_OWNDC, BLACK_BRUSH, 0);
9080 $
if (!wndClass)
return (HWND) NULL;
9083 centered? screen.cx/2 - size.cx/2 : CW_USEDEFAULT,
9084 centered? screen.cy/2 - size.cy/2 : CW_USEDEFAULT,
9085 size.cx, size.cy, NULL, NULL, NULL, NULL);
9087 {$
return TX_DEBUG_ERROR (
"Cannot create canvas: CreateWindowEx() failed"), (HWND) NULL; }
9089 $ HMENU menu = GetSystemMenu (
txWindow(),
false);
9092 $ AppendMenu (menu, MF_SEPARATOR, 0, NULL)
asserted;
9093 $ AppendMenu (menu, MF_STRING, _TX_IDM_CONSOLE,
"Show &Console")
asserted;
9094 $ AppendMenu (menu, MF_STRING, _TX_IDM_ABOUT, "&About...")
asserted;
9101 const
char* txRegisterClass (const
char classId[], WNDPROC wndProc,
unsigned style,
int backBrush,
int wndExtra)
9107 $ _tx_snprintf_s (name,
sizeof (name) - 1,
"/*---[TXLib]-[%s]------------ "
9109 "-------------[%s]-[TXLib]---*/",
9110 classId, (
unsigned long) GetTickCount(), classId);
9111 $ WNDCLASS wc = {
sizeof (wc) };
9113 $ wc.lpszClassName = name;
9114 $ wc.lpfnWndProc = wndProc;
9116 $ wc.cbWndExtra = (wndExtra + 1) * (
int)
sizeof (long);
9118 $ wc.hCursor = LoadCursor (NULL, IDC_ARROW);
9119 $ wc.hbrBackground = (HBRUSH) Win32::GetStockObject (backBrush);
9121 $ ATOM atom = RegisterClass (&wc);
9122 if (!atom) {$
TX_DEBUG_ERROR (
"RegisterClass (\"%s\") failed", name);
return 0; }
9124 $
return (
const char*)(uintptr_t) atom;
9129 inline bool _txCanvas_OK()
9131 return _txCanvas_ThreadId &&
9133 _txCanvas_BackBuf[0] &&
9134 _txCanvas_BackBuf[1] &&
9140 int _txCanvas_SetRefreshLock (
int count)
9142 $8
int oldCount = _txCanvas_RefreshLock;
9144 $ _txCanvas_RefreshLock = count;
9148 $
if ((_txCanvas_RefreshLock <= 0 || oldCount <= 0) && wnd)
9149 {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
9156 HICON _txCreateTXIcon (
int size)
9158 $8
if (_TX_ARGUMENT_FAILED (size == 32 || size == 16))
return NULL;
9160 $
const unsigned char image32 [32*32+1] =
9161 "00000000000000000000000000000000""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""0F0000000000000000000000000000F0""0F0000000000000000000000000000F0"
9162 "0F0000000000000099999999999900F0""0F0000000000000090300333330900F0""0F0000000990000090000000000900F0""0F00000099990000900BB000000900F0"
9163 "0F0000039999000090B00090900900F0""0F0000009999000090B00999990900F0""0F00000009903799900BB090900900F0""0F000000009BB70090000010000900F0"
9164 "0F0000000B90000090000000000900F0""0F000000B0B0000099999999999900F0""0F00007B30B0000090000000000000F0""0F00007300B0000090000000000000F0"
9165 "0F00000000B3000090000000000000F0""0F0000000B0B000090000000000000F0""0F000000B303B00090000000000000F0""0F000003B000B00090000000000000F0"
9166 "0F00003B00003B0090000000000000F0""0F0000300000030090000000000000F0""0F0000000448888888888844000000F0""0F00004886E6E6E60E66E6EEEE4400F0"
9167 "0F4488866E0E60E00660E06E66EEE4F0""0F868806E06E06E666E66E00E06EE6F0""0F08606E66E0066000E006E66E00E6F0""0F8666E006600E00006600E006E00EF0"
9168 "0F000E066888888888888888606660F0""0F66EEE6EE000E00000E00086EEEE6F0""0FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0""00000000000000000000000000000000";
9170 $
const unsigned char image16 [16*16+1] =
9171 "0000000000000000""0000000999999990""0009000900000090""0099900909973090""0059700909009390""0009799909973090""0099000900000090""0959330999999990"
9172 "0709500900000000""0095930900000000""0090393900000000""0790073900000000""0900000900000000""000EE6E6E6E6E000""0EE6E6E6E6E6EEE0""0000000000000000";
9174 $
const COLORREF pal[
'F'-
'0'+1] = { 0x000000, 0x002b2b, 0x555500, 0x005555, 0x808000, 0x008080, 0xaaaa00, 0x00aaaa, 0xd5d500, 0x00d5d5, 0,0,0,0,0,0,0,
9175 0xffff00, 0x00ffff, 0xffffaa, 0xaaffff, 0xd5d500, 0xffffff };
9177 $
const unsigned char* image = (size == 32)? image32 : image16;
9179 $ POINT sz = { size, size };
9180 $ HDC dcMask = _txBuffer_Create (
txWindow(), &sz);
assert (dcMask);
9181 $ HDC dcColor = _txBuffer_Create (
txWindow(), &sz);
assert (dcColor);
9183 $
for (
int i = 0; i < size*size; i++)
9185 assert (
In (std::nomeow, image[i],
'0',
'9') ||
9186 In (std::nomeow, image[i],
'A',
'F'));
9188 Win32::SetPixel (dcColor, i % size, i / size, pal [image[i] -
'0']);
9191 $ ICONINFO info = {
true, 0, 0, (HBITMAP) Win32::GetCurrentObject (dcMask, OBJ_BITMAP),
9192 (HBITMAP) Win32::GetCurrentObject (dcColor, OBJ_BITMAP) };
9194 $ HICON icon = CreateIconIndirect (&info);
9197 $ _txBuffer_Delete (&dcMask)
asserted;
9198 $ _txBuffer_Delete (&dcColor)
asserted;
9212 LRESULT CALLBACK _txCanvas_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
9214 #if defined (_TX_ALLOW_TRACE)
9216 int inTX = _txLoc::Cur.inTX++;
9218 if (_txLoc::Cur.trace) _txTrace (__FILE__, __LINE__,
__TX_FUNCTION__,
"%*s" "0x%X <- 0x%03X (0x%08X, 0x%08lX)",
9219 2 * (_txLoc::Cur.inTX - 1),
"", wnd, msg, wpar, lpar);
9220 _txLoc::Cur.inTX = inTX;
9224 $8
if (msg == WM_KEYDOWN && wpar == VK_F12 &&
9225 GetKeyState (VK_SHIFT) && GetKeyState (VK_CONTROL) && GetKeyState (VK_MENU))
9227 $ _txCanvas_OnCmdABOUT (wnd, wpar);
9228 $
return DefWindowProc (wnd, msg, wpar, lpar);
9231 WNDPROC altWndProc = _txAltWndProc;
9234 $ LRESULT res = altWndProc (wnd, msg, wpar, lpar);
9235 $
if (res)
return res;
9238 static bool bkErased =
false;
9242 case WM_CREATE: {$ _txCanvas_OnCREATE (wnd);
return 0; }
9244 case WM_CLOSE: {$
if (_txCanvas_OnCLOSE (wnd))
break;
else return 0; }
9245 case WM_DESTROY: {$ _txCanvas_OnDESTROY (wnd);
return 0; }
9247 case WM_ERASEBKGND: {$
if (!bkErased) { bkErased =
true;
break; }
else return 1; }
9248 case WM_SIZE: {$ bkErased =
false;
break; }
9250 case WM_PAINT: {$ _txCanvas_OnPAINT (wnd);
return 0; }
9252 case WM_TIMER: {$ _txCanvas_OnTIMER (wnd, wpar);
return 0; }
9254 case WM_KEYDOWN: {$ _txCanvas_OnKEYDOWN (wnd, wpar, lpar);
return 0; }
9255 case WM_CHAR: {$ _txCanvas_OnCHAR (wnd, wpar, lpar);
return 0; }
9258 case WM_LBUTTONDOWN:
9260 case WM_RBUTTONDOWN:
9262 case WM_MBUTTONDOWN:
9263 case WM_MOUSEMOVE: {$ _txCanvas_OnMOUSEMOVE (wnd, wpar, lpar);
return 0; }
9265 case WM_MOUSELEAVE: {$ _txCanvas_OnMOUSELEAVE (wnd);
return 0; }
9267 case _TX_WM_CREATEWND: {$ _txCanvas_OnCREATEWND (wnd, wpar, lpar);
return 0; }
9268 case _TX_WM_DESTROYWND: {$ _txCanvas_OnDESTROYWND (wnd, wpar, lpar);
return 0; }
9273 if (msg == WM_SYSCOMMAND)
switch (wpar)
9275 case _TX_IDM_ABOUT: {$ _txCanvas_OnCmdABOUT (wnd, wpar);
return 0; }
9276 case _TX_IDM_CONSOLE: {$ _txCanvas_OnCmdCONSOLE (wnd, wpar);
return 0; }
9281 $
return DefWindowProc (wnd, msg, wpar, lpar);
9286 bool _txCanvas_OnCREATE (HWND wnd)
9288 $8
if (_TX_ARGUMENT_FAILED (wnd))
return false;
9290 $ _txCanvas_BackBuf[0] = _txBuffer_Create (wnd, NULL, NULL, &_txCanvas_Pixels);
assert (_txCanvas_BackBuf[0]);
9291 $ _txCanvas_BackBuf[1] = _txBuffer_Create (wnd, NULL, NULL, NULL);
assert (_txCanvas_BackBuf[1]);
9294 $
assert (_txCanvas_RefreshTimer);
9296 $ _txCanvas_UserDCs = new ::std::vector <HDC>;
9298 $ _txCanvas_Window = wnd;
9307 bool _txCanvas_OnDESTROY (HWND wnd)
9309 $8
if (_TX_ARGUMENT_FAILED (wnd))
return false;
9313 $ PostQuitMessage (_txRunning? WM_DESTROY : EXIT_SUCCESS);
9315 $
if (!_txCanvas_Window)
return false;
9323 $
bool locked =
false;
9325 $
if (!locked)
TX_DEBUG_ERROR (
"Cannot lock GDI to free resources");
9329 $
if (_txCanvas_UserDCs && !_txCanvas_UserDCs->empty())
9331 $ txNotifyIcon (NIIF_ERROR, NULL,
"Вы забыли освободить %d HDC.", (
int) _txCanvas_UserDCs->size());
9334 $
for (
size_t i = 0; i < _txCanvas_UserDCs->size(); i++) _txBuffer_Delete (&_txCanvas_UserDCs->at (i));
9335 $ _txCanvas_UserDCs->clear();
9338 $
delete _txCanvas_UserDCs; _txCanvas_UserDCs = NULL;
9342 $
if (_txCanvas_RefreshTimer) KillTimer (wnd, _txCanvas_RefreshTimer)
asserted;
9344 $
if (_txCanvas_BackBuf[1]) _txBuffer_Delete (&_txCanvas_BackBuf[1])
asserted;
9345 $
if (_txCanvas_BackBuf[0]) _txBuffer_Delete (&_txCanvas_BackBuf[0])
asserted;
9346 $ _txCanvas_Pixels = NULL;
9352 $ _txCanvas_Window = NULL;
9359 bool _txCanvas_OnCLOSE (HWND wnd)
9361 $8
if (_TX_ARGUMENT_FAILED (wnd))
return false;
9362 $
if (!_txCanvas_OK())
return false;
9364 $
if (_txMain && _txRunning &&
9365 txMessageBox (
"Функция main() не завершена. Программа все еще работает. Прервать аварийно?\n\n"
9366 "Лучше подождать, когда main() завершится - это отображается в заголовке окна.",
9373 bool _txCanvas_OnTIMER (HWND wnd, WPARAM)
9375 $8
if (_TX_ARGUMENT_FAILED (wnd))
return false;
9377 $
if (_txCanvas_RefreshLock > 0 || !_txRunning)
return false;
9379 $ InvalidateRect (wnd, NULL,
false)
asserted;
9387 bool _txCanvas_OnPAINT (HWND wnd)
9389 $8
if (_TX_ARGUMENT_FAILED (wnd))
return false;
9390 $
if (!_txCanvas_OK())
return false;
9392 $
bool forceRedraw = GetAsyncKeyState (VK_MENU) && GetAsyncKeyState (VK_CONTROL) &&
9393 GetAsyncKeyState (VK_SHIFT) && GetAsyncKeyState (VK_SNAPSHOT);
9395 $ PAINTSTRUCT ps = {};
9396 $ HDC wndDc = BeginPaint (wnd, &ps);
9397 $
if (!wndDc)
return false;
9399 $ HDC dc0 = _txCanvas_BackBuf[0],
9400 dc1 = _txCanvas_BackBuf[1];
9403 $ GetClientRect (wnd, &r)
asserted;
9404 $ POINT wndSize = { r.right - r.left, r.bottom - r.top };
9408 $
if ((_txCanvas_RefreshLock <= 0 || forceRedraw) &&
9411 $ Win32::BitBlt (dc1, 0, 0, dcSize.x, dcSize.y, dc0, 0, 0, SRCCOPY);
9413 $ _txConsole_Draw (dc1);
9424 $
if (_txCanvas_RefreshLock != 100500)
9428 $
_txSwapBuffers (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY);
9430 else if (dcSize.x == wndSize.x && dcSize.y == wndSize.y)
9432 $ Win32::BitBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, SRCCOPY);
9436 $ Win32::SetStretchBltMode (wndDc, HALFTONE);
9437 $ Win32::StretchBlt (wndDc, 0, 0, wndSize.x, wndSize.y, dc1, 0, 0, dcSize.x, dcSize.y, SRCCOPY);
9448 bool _txCanvas_OnKEYDOWN (HWND, WPARAM vk, LPARAM info)
9450 $8 INPUT_RECORD evt = {};
9452 $ evt.EventType = KEY_EVENT;
9453 $ evt.Event.KeyEvent.bKeyDown =
true;
9454 $ evt.Event.KeyEvent.wRepeatCount = 1;
9455 $ evt.Event.KeyEvent.uChar.AsciiChar = (char) MapVirtualKey ((WORD) vk, 2);
9456 $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16);
9457 $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) vk;
9458 $ evt.Event.KeyEvent.dwControlKeyState = (DWORD) (info & (1 << 24))? ENHANCED_KEY : 0;
9460 $
if (evt.Event.KeyEvent.uChar.AsciiChar)
return false;
9462 $ DWORD written = 0;
9463 $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written);
9470 bool _txCanvas_OnCHAR (HWND, WPARAM ch, LPARAM info)
9472 $8 INPUT_RECORD evt = {};
9474 $ evt.EventType = KEY_EVENT;
9475 $ evt.Event.KeyEvent.bKeyDown =
true;
9476 $ evt.Event.KeyEvent.wRepeatCount = 1;
9477 $ evt.Event.KeyEvent.uChar.AsciiChar = (char) (ch);
9478 $ evt.Event.KeyEvent.wVirtualScanCode = (WORD) (info >> 16);
9479 $ evt.Event.KeyEvent.wVirtualKeyCode = (WORD) MapVirtualKey ((WORD) (info >> 16), 3);
9480 $ evt.Event.KeyEvent.dwControlKeyState = 0;
9482 $ DWORD written = 0;
9483 $ WriteConsoleInput (GetStdHandle (STD_INPUT_HANDLE), &evt, 1, &written);
9490 bool _txCanvas_OnMOUSEMOVE (HWND wnd, WPARAM buttons, LPARAM coords)
9492 $8
if (_TX_ARGUMENT_FAILED (wnd))
return false;
9493 $
if (!_txCanvas_OK())
return false;
9495 $
if (_txMousePos.x == -1 && _txMousePos.y == -1)
9497 $ TRACKMOUSEEVENT track = {
sizeof (track), TME_HOVER | TME_LEAVE, wnd, HOVER_DEFAULT };
9498 $ TrackMouseEvent (&track);
9501 $ _txMousePos.x = LOWORD (coords);
9502 $ _txMousePos.y = HIWORD (coords);
9503 $ _txMouseButtons = (unsigned) buttons;
9510 bool _txCanvas_OnMOUSELEAVE (HWND)
9512 $8 _txMousePos.x = -1;
9513 $ _txMousePos.y = -1;
9514 $ _txMouseButtons = 0;
9521 bool _txCanvas_OnCREATEWND (HWND, WPARAM, LPARAM lpar)
9523 $8
if (_TX_ARGUMENT_FAILED (lpar))
return false;
9525 $
const CREATESTRUCT* create = (CREATESTRUCT*) lpar;
9527 $ HWND wnd = CreateWindowEx (create->dwExStyle, create->lpszClass, create->lpszName, create->style,
9528 create->x, create->y, create->cx, create->cy,
9529 create->hwndParent, create->hMenu, NULL, create->lpCreateParams);
9531 $ *(HWND*) create->hInstance = wnd;
9538 bool _txCanvas_OnDESTROYWND (HWND, WPARAM, LPARAM lpar)
9540 $8
if (_TX_ARGUMENT_FAILED (lpar))
return false;
9542 $ DestroyWindow ((HWND) lpar);
9549 bool _txCanvas_OnCmdCONSOLE (HWND wnd, WPARAM cmd)
9551 $8
if (_TX_ARGUMENT_FAILED (wnd))
return false;
9553 $ HWND console = Win32::GetConsoleWindow();
9554 $
if (!console)
return false;
9556 $
bool visible = !!IsWindowVisible (console);
9558 $ ShowWindow (console, visible? SW_HIDE : SW_SHOW);
9560 $ visible = !!IsWindowVisible (console);
9561 $ CheckMenuItem (GetSystemMenu (wnd,
false), (
int) cmd, visible? MF_CHECKED : MF_UNCHECKED);
9568 bool _txCanvas_OnCmdABOUT (HWND, WPARAM)
9572 #if defined (__MODULE)
9573 #define ABOUT_NAME_ __MODULE
9575 #define ABOUT_NAME_ "TXLib"
9578 #if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR)
9581 #define __MODULE "TXLib" "\n" "#define __MODULE to set the name.\n"
9585 #define __VERSION "(0.000000000)." "\n" "#define __VERSION to set the string value.\n"
9588 #ifndef __DESCRIPTION
9589 #define __DESCRIPTION "(Да, мне лень задать описание)." "\n" "#define __DESCRIPTION to override project role.\n"
9593 #define __AUTHOR "(Непонятно кто)." "\n" "#define __AUTHOR to override this name."
9601 $ _tx_snprintf_s (text,
sizeof (text) - 1,
9605 #
if defined (__MODULE) || defined (__VERSION) || defined (__DESCRIPTION) || defined (__AUTHOR)
9606 __MODULE
" version " __VERSION
"\n" __DESCRIPTION
"\n" "Copyright (c) " __AUTHOR
"\n"
9608 "Здесь могла бы быть Ваша реклама :)\n"
9609 "#define __MODULE to \"your program name\" before including TXLib.h to use this billboard...\n"
9612 "\n" "%s", _txAppInfo());
9614 $
txMessageBox (text,
"About " ABOUT_NAME_, MB_ICONINFORMATION);
9636 HWND _txConsole_Attach()
9638 $1 HWND console = Win32::GetConsoleWindow();
9646 $ console = Win32::GetConsoleWindow();
9647 $
if (!console)
return NULL;
9651 $
static bool done =
false;
9652 $
if (done)
return console;
9654 $ _txConsole_SetUnicodeFont();
9656 $
if (!_txIsConsoleSubsystem())
9657 {$ txReopenStdio(); }
9668 const char locale[] ,
const wchar_t wLocale[] )
9670 $1
int oldPage = GetConsoleOutputCP();
9676 $ SetConsoleCP (codepage);
9677 $ SetConsoleOutputCP (codepage);
9687 $ setlocale (LC_ALL, locale);
9688 $ setlocale (LC_NUMERIC,
"C");
9693 $
const bool wine = !!Win32::wine_get_version;
9695 $
if (wLocale && !wine)
9697 $ _wsetlocale (LC_ALL, wLocale);
9698 $ _wsetlocale (LC_NUMERIC, L
"C");
9710 void txReopenStdio()
9716 $ *stdin = *_fdopen (_open_osfhandle ((intptr_t) GetStdHandle (STD_INPUT_HANDLE), _O_TEXT),
"r");
9717 $ fflush (stdout); *stdout = *_fdopen (_open_osfhandle ((intptr_t) GetStdHandle (STD_OUTPUT_HANDLE), _O_TEXT),
"w");
9718 $ fflush (stderr); *stderr = *_fdopen (_open_osfhandle ((intptr_t) GetStdHandle (STD_ERROR_HANDLE), _O_TEXT),
"w");
9721 $ *stdin = *_fdopen (STDIN_FILENO,
"r");
9722 $ fflush (stdout); *stdout = *_fdopen (STDOUT_FILENO,
"w");
9723 $ fflush (stderr); *stderr = *_fdopen (STDERR_FILENO,
"w");
9727 $ setvbuf (stdin, NULL, _IONBF, 0);
9728 $ setvbuf (stdout, NULL, _IONBF, 0);
9729 $ setvbuf (stderr, NULL, _IONBF, 0);
9731 $ ::std::ios::sync_with_stdio();
9736 inline bool _txConsole_OK()
9738 return Win32::GetConsoleWindow() != NULL;
9743 bool _txConsole_Detach (
bool activate)
9745 $1 HWND console = Win32::GetConsoleWindow();
9746 $
if (!console)
return false;
9748 $ EnableWindow (console,
true);
9749 $ ShowWindow (console, SW_SHOW);
9753 $ SetForegroundWindow (console);
9754 $ BringWindowToTop (console);
9757 $
return !!FreeConsole();
9762 bool _txConsole_Draw (HDC dc)
9764 $8
if (_TX_HDC_FAILED (dc))
return false;
9766 $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
9768 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
9769 $ BOOL ok = GetConsoleScreenBufferInfo (out, &con);
9770 $
if (!ok)
return false;
9772 $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1,
9773 con.srWindow.Bottom - con.srWindow.Top + 1 };
9775 $ SIZE fontSz = { 12, 16 };
9776 $ Win32::GetTextExtentPoint32 (dc,
"W", 1, &fontSz)
asserted;
9778 $ COLORREF pal [16] = { 0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0,
9779 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF };
9781 $
for (
short y = 0; y < size.y; y++)
9785 COORD coord = { (short) (con.srWindow.Left), (short) (y + con.srWindow.Top) };
9788 if (!ReadConsoleOutputCharacter (out, chr,
sizearr (chr) - 1, coord, &read))
continue;
9789 if (!ReadConsoleOutputAttribute (out, atr,
sizearr (atr) - 1, coord, &read))
continue;
9791 for (
int x = 0, xEnd = size.x; x < size.x; x = xEnd)
9793 Win32::SetTextColor (dc, pal [ atr[x] & 0x0F]);
9794 Win32::SetBkColor (dc, pal [(atr[x] >> 4) & 0x0F]);
9795 Win32::SetBkMode (dc, (atr[x] & 0xF0)? OPAQUE : TRANSPARENT);
9797 for (xEnd = x+1; xEnd < size.x && atr[xEnd] == atr[x]; xEnd++) {;}
9799 Win32::TextOut (dc,
ROUND (fontSz.cx * (x + con.srWindow.Left)),
9804 $ Win32::SetTextColor (dc, pal [ con.wAttributes & 0x0F]);
9805 $ Win32::SetBkColor (dc, pal [(con.wAttributes >> 4) & 0x0F]);
9806 $ Win32::SetBkMode (dc, TRANSPARENT);
9808 $
if (_txConsole_IsBlinking &&
9809 In (con.dwCursorPosition, con.srWindow) &&
9811 GetForegroundWindow() ==
txWindow())
9813 $ Win32::TextOut (dc,
ROUND (fontSz.cx * (con.dwCursorPosition.X - con.srWindow.Left)),
9814 ROUND (fontSz.cy * (con.dwCursorPosition.Y - con.srWindow.Top)) + 1,
9825 bool _txConsole_SetUnicodeFont()
9827 $
const bool wine = !!Win32::wine_get_version;
9831 $ Win32::GetNumberOfConsoleFonts = NULL;
9832 $ Win32::GetCurrentConsoleFont = NULL;
9833 $ Win32::SetConsoleFont = NULL;
9840 $1
if (Win32::GetCurrentConsoleFontEx && Win32::SetCurrentConsoleFontEx)
9842 $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
9844 $ Win32::CONSOLE_FONT_INFOEX info = {
sizeof (info) };
9845 $
if (!Win32::GetCurrentConsoleFontEx (out,
false, &info))
return false;
9847 $ info.FontFamily = 0x36;
9848 $
if (!*info.FaceName) info.dwFontSize.Y = (SHORT) (info.dwFontSize.Y + 2);
9850 $
if (wcsncmp (info.FaceName, L
"Consolas",
sizearr (info.FaceName)) != 0)
9853 $
return !!Win32::SetCurrentConsoleFontEx (out,
false, &info);
9858 $
const unsigned uniFont = 10;
9859 $
const unsigned uniSize = 20;
9864 $ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
9866 $
unsigned fonts = _TX_CALL (Win32::GetNumberOfConsoleFonts, ());
9867 $
if (fonts && fonts <= uniFont)
9869 $ HRESULT init = Win32::CoInitialize (NULL);
9872 $
char link [MAX_PATH] =
"";
9873 $
getenv_s (&sz, link,
sizeof (link) - 1,
"TEMP");
9874 $
strncat_s (link,
sizeof (link),
"\\~txLink.lnk",
sizeof (link) - 1);
9876 $
char comspec [MAX_PATH] =
"";
9877 $
getenv_s (&sz, comspec,
sizeof (comspec),
"COMSPEC");
9879 $ (void) _unlink (link);
9881 $ _txCreateShortcut (link, comspec,
"/c exit", NULL, NULL, SW_SHOWMINNOACTIVE, NULL, 0, uniSize)
asserted;
9883 $ ok = (Win32::ShellExecuteA (NULL, NULL, link, NULL, NULL, SW_SHOWMINNOACTIVE) > (
void*)32);
9884 if (ok) {$ _txWaitFor (FindWindow (NULL,
"~txLink"),
_TX_TIMEOUT); }
9886 $ (void) _unlink (link);
9888 $
if (init == S_OK) Win32::CoUninitialize();
9893 $ Win32::CONSOLE_FONT_INFO cur = {};
9894 $ _TX_CALL (Win32::GetCurrentConsoleFont, (out,
false, &cur));
9896 $ ok &= (cur.nFont >= uniFont);
9897 if (!ok) {$ ok &= _TX_CALL (Win32::SetConsoleFont, (out, uniFont)); }
9899 $ HWND console = Win32::GetConsoleWindow();
9900 $ InvalidateRect (console, NULL,
false);
9901 $ UpdateWindow (console);
9909 #define _TX_TRY { goto __tx_try; } __tx_try: { int __tx_error = S_OK; (void)__tx_error;
9910 #define _TX_CHECKED( cmd ) { if (FAILED (__tx_error = (cmd))) goto __tx_catch; }
9911 #define _TX_FAIL { __tx_error = E_FAIL; goto __tx_catch; }
9912 #define _TX_RETRY { __tx_error = S_OK; goto __tx_try; }
9913 #define _TX_OK ( SUCCEEDED (__tx_error) )
9914 #define _TX_CATCH goto __tx_finally; __tx_catch:
9915 #define _TX_RETURN goto __tx_finally;
9916 #define _TX_FINALLY __tx_finally:
9917 #define _TX_ENDTRY }
9924 bool _txCreateShortcut (
const char shortcutName[],
9925 const char fileToLink[],
const char args[] ,
const char workDir[] ,
9926 const char description[] ,
int cmdShow ,
const char iconFile[] ,
int iconIndex ,
9927 int fontSize , COORD bufSize , COORD wndSize , COORD wndOrg )
9929 $1
if (_TX_ARGUMENT_FAILED (shortcutName && *shortcutName))
return false;
9930 $
if (_TX_ARGUMENT_FAILED (fileToLink && *fileToLink))
return false;
9932 $ IShellLink* shellLink = NULL;
9933 $ Win32::IShellLinkDataList* dataList = NULL;
9934 $ IPersistFile* file = NULL;
9936 $ HRESULT init = Win32::CoInitialize (NULL);
9940 $ _TX_CHECKED (Win32::CoCreateInstance (CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, Win32::IID_IShellLink, (
void**) &shellLink));
9941 $
if (!shellLink) _TX_FAIL;
9943 $ shellLink->SetPath (fileToLink);
9944 $ shellLink->SetArguments (args);
9945 $ shellLink->SetWorkingDirectory (workDir);
9946 $ shellLink->SetDescription (description);
9947 $ shellLink->SetShowCmd (cmdShow);
9948 $ shellLink->SetIconLocation (iconFile, iconIndex);
9950 $ _TX_CHECKED (shellLink->QueryInterface (Win32::IID_IShellLinkDataList, (
void**) &dataList));
9951 $
if (!dataList) _TX_FAIL;
9953 $ Win32::NT_CONSOLE_PROPS props =
9954 {{
sizeof (props), NT_CONSOLE_PROPS_SIG},
9956 FOREGROUND_LIGHTGRAY,
9957 FOREGROUND_MAGENTA | BACKGROUND_WHITE,
9958 {bufSize.X, bufSize.Y},
9959 {wndSize.X, wndSize.Y},
9960 {wndOrg.X, wndOrg.Y},
9963 {0, (short) fontSize},
9964 0x36, 400, L
"Lucida Console",
9969 {0x000000, 0x800000, 0x008000, 0x808000, 0x000080, 0x800080, 0x008080, 0xC0C0C0,
9970 0x808080, 0xFF0000, 0x00FF00, 0xFFFF00, 0x0000FF, 0xFF00FF, 0x00FFFF, 0xFFFFFF}
9973 $ _TX_CHECKED (dataList->AddDataBlock (&props));
9975 $ _TX_CHECKED (shellLink->QueryInterface (Win32::IID_IPersistFile, (
void**) &file));
9976 $
if (!file) _TX_FAIL;
9978 $
wchar_t wName[MAX_PATH] = L
"";
9979 $ MultiByteToWideChar (
_TX_CODEPAGE, 0, shortcutName, -1, wName, MAX_PATH) || ZeroMemory (wName,
sizeof (wName));
9981 $ _TX_CHECKED (file->Save (wName,
true));
9987 $
if (file) file ->Release();
9988 $
if (dataList) dataList ->Release();
9989 $
if (shellLink) shellLink->Release();
9991 $
if (init == S_OK) Win32::CoUninitialize();
10010 HDC _txBuffer_Create (HWND wnd,
const POINT* size , HBITMAP bitmap , RGBQUAD** pixels )
10014 $ HDC wndDC = GetDC (wnd);
10015 $
if (!wndDC)
return NULL;
10017 $ POINT sz = { 1, 1 };
10018 $
if (size) sz = *size;
10020 $
if (!size && wnd)
10023 $ GetClientRect (wnd, &r)
asserted;
10025 $ sz.x = r.right - r.left;
10026 $ sz.y = r.bottom - r.top;
10031 $ BITMAP bmap = {};
10032 $ Win32::GetObject (bitmap,
sizeof (bmap), &bmap)
asserted;
10034 $ sz.x = bmap.bmWidth;
10035 $ sz.y = bmap.bmHeight;
10038 $ HDC dc = Win32::CreateCompatibleDC (wndDC);
10039 $
if (!dc)
TX_DEBUG_ERROR (
"Cannot create buffer: CreateCompatibleDC() failed");
10041 $ BITMAPINFO info = {{
sizeof (info), sz.x, sz.y, 1, WORD (
sizeof (RGBQUAD) * 8), BI_RGB }};
10043 $ HBITMAP bmap = bitmap? bitmap : Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (
void**) pixels, NULL, 0);
10044 $
if (!bmap)
TX_DEBUG_ERROR (
"Cannot create buffer: CreateCompatibleBitmap() failed");
10046 $ Win32::SelectObject (dc, bmap)
asserted;
10048 $
if (!bitmap) Win32::PatBlt (dc, 0, 0, sz.x, sz.y, BLACKNESS)
asserted;
10050 $ ReleaseDC (wnd, wndDC)
asserted;
10057 bool _txBuffer_Delete (HDC* dc)
10059 $1
if (_TX_ARGUMENT_FAILED (dc))
return false;
10060 $
if (!*dc)
return false;
10062 $
if (_TX_HDC_FAILED (*dc))
return false;
10064 $
if (!Win32::GetObjectType (Win32::GetCurrentObject (*dc, OBJ_BITMAP)))
return false;
10068 $ _txBuffer_Select (Win32::GetStockObject (NULL_PEN), *dc)
asserted;
10069 $ _txBuffer_Select (Win32::GetStockObject (NULL_BRUSH), *dc)
asserted;
10070 $ _txBuffer_Select (Win32::GetStockObject (SYSTEM_FONT), *dc)
asserted;
10071 $ _txBuffer_Select (_txStockBitmap, *dc);
10073 $ Win32::DeleteObject (Win32::GetCurrentObject (*dc, OBJ_BITMAP))
asserted;
10084 bool _txBuffer_Select (HGDIOBJ obj, HDC dc )
10086 $1
if (_TX_ARGUMENT_FAILED (obj))
return false;
10087 $
if (_TX_HDC_FAILED (dc))
return false;
10089 $
if (!Win32::GetObjectType (obj))
TX_DEBUG_ERROR (
"Invalid GDI object type");
10093 $ obj = Win32::SelectObject (dc, obj);
10094 $
if (obj) Win32::DeleteObject (obj);
10096 $
return obj != NULL;
10109 const char* _txError (
const char file[] ,
int line ,
const char func[] ,
unsigned color ,
10110 const char msg [] , ...)
10112 $1 va_list arg; va_start (arg, msg);
10113 $$
const char* what = _txProcessError (file, line, func, color, msg, arg);
10116 if (!(msg && msg[0] ==
'\a'))
return what;
10129 void _txOnSignal (
int sig ,
int fpe )
10131 $1
if (!sig && !fpe)
10133 $ signal (SIGSEGV, (
void(*)(
int))(uintptr_t)_txOnSignal) != SIG_ERR
asserted;
10134 $ signal (SIGFPE, (
void(*)(
int))(uintptr_t)_txOnSignal) != SIG_ERR
asserted;
10135 $ signal (SIGABRT, (
void(*)(
int))(uintptr_t)_txOnSignal) != SIG_ERR
asserted;
10136 $ signal (SIGILL, (
void(*)(
int))(uintptr_t)_txOnSignal) != SIG_ERR
asserted;
10137 $ signal (SIGTERM, (
void(*)(
int))(uintptr_t)_txOnSignal) != SIG_ERR
asserted;
10143 #define GET_DESCR_(str, code, descr) case (code): {$ (str) = " " #code ": " descr; break; }
10145 $
const char* sSig =
"Неизвестный тип сигнала ;
$ switch (sig)
{
GET_DESCR_ (sSig, SIGSEGV, "Доступ по неверному указателю. Ставьте ассерты!")
GET_DESCR_ (sSig, SIGILL, "Попытка выполнить недопустимую операцию. Проверьте указатели на функции.")
GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si";
10149 GET_DESCR_ (sSig, SIGSEGV,
"Доступ по неверному указателю. Ставьте ассерты!")
10150 GET_DESCR_ (sSig, SIGILL, "Попытка выполнить недопустимую операцию. Проверьте указатели на функции.")
GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si выполнить недопустимую операцию. Проверьте указатели на функции.")
GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si недопустимую операцию. Проверьте указатели на функции.")
GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si указатели на функции.")
GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si на функции ")
GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10151 GET_DESCR_ (sSig, SIGABRT, "Аварийное завершение программы, вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si завершение программы вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si, вызвана функция abort().")
GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si функция abort().")
10152 GET_DESCR_ (sSig, SIGTERM, "Получен сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si сигнал принудительного завершения программы.")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si завершения программы ")
GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10153 GET_DESCR_ (sSig, SIGFPE, "Грубая ошибка в вычислениях.")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si вычислениях ")
default: break;
}
$ const char* sFPE = "";
#if defined (_MSC_VER)
// MSVC provides the FPE code as a MS extension.
// See: https://msdn.microsoft.com/ru-ru/library/xdkz3x12.aspx
$ if (sig == SIGFPE) switch (fpe)
{
GET_DESCR_ (sFPE, _FPE_INVALID, "Результат неверен.")
GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10157 $ const
char* sFPE = "";
10159 #if defined (_MSC_VER)
10164 $
if (sig == SIGFPE)
switch (fpe)
10166 GET_DESCR_ (sFPE, _FPE_INVALID,
"Результат неверен.")
10167 GET_DESCR_ (sFPE, _FPE_DENORMAL, "Денормализация.")
10168 GET_DESCR_ (sFPE, _FPE_ZERODIVIDE, "Деление на ноль.")
GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si на ноль.")
10169 GET_DESCR_ (sFPE, _FPE_OVERFLOW, "Результат слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si слишком большой.")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si большой ")
GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10170 GET_DESCR_ (sFPE, _FPE_UNDERFLOW, "Результат слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si слишком маленький.")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si маленький ")
GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10171 GET_DESCR_ (sFPE, _FPE_INEXACT, "Результат неточен.")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si неточен ")
GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа.")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10172 GET_DESCR_ (sFPE, _FPE_UNEMULATED, "Операция не поддерживается.")
10173 GET_DESCR_ (sFPE, _FPE_SQRTNEG, "Квадратный корень из отрицательного числа ")
GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10174 GET_DESCR_ (sFPE, _FPE_STACKOVERFLOW, "Переполнение стека сопроцессора.")
GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si сопроцессора.")
10175 GET_DESCR_ (sFPE, _FPE_STACKUNDERFLOW, "В стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si стеке сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si сопроцессора не хватает данных.")
GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si данных.")
10176 GET_DESCR_ (sFPE, _FPE_EXPLICITGEN, "Явный вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si вызов исключения.")
default: break;
}
#else
$ fpe = 0;
#endif
#undef GET_DESCR_
$ signal (sig, (void(*)(int))(uintptr_t)_txOnSignal);
$ Win32::_fpreset();
$ _TX_UNEXPECTED ("\a\t"
"signal (%d, 0x%02X):%s%s "
"%s%s"
"С помощью функции signal() вы можете сами обработать эту ошибку.",
sig, (unsigned) fpe, sSig, sFPE,
((_txDumpSE[1] == '\n')? "" : "\n\n"), _txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnTerminate()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
// From: http://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/libsupc%2B%2B/vterminate.cc
$1 static int terminating = 0;
if (terminating++) {$ _TX_UNEXPECTED ("\a" "std::terminate() вызвана рекурсивно."); return; }
$ if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("\t\a"
"std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
"или другая фатальная ошибка C++. "
"%s"
"Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
"разбирайтесь, в чем дело.\n\n"
"С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnUnexpected()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1 if (!*_txDumpSE)
{$ _txDumpExceptionCPP (_txDumpSE + 1, sizeof (_txDumpSE) - 2); }
$ _TX_UNEXPECTED ("std::unexpected(): Необработанное исключение.\n\n"
"Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
"спецификацию исключений для функций, проверьте, не нарушена ли она."
"%s"
"С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
//-----------------------------------------------------------------------------------------------------------------
int _txOnMatherr (_exception* exc)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, exc);
$1 assert (exc);
const char* sType = "Неизвестный тип исключения";
#if !defined (__CYGWIN__)
#define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
$ switch (exc->type)
{
GET_DESCR_ (_DOMAIN, "Нарушение области определения");
GET_DESCR_ (_SING, "Сингулярность аргумента");
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si исключения.")
10186 $ signal (sig, (
void(*)(
int))(uintptr_t)_txOnSignal);
10188 $ Win32::_fpreset();
10190 $ _TX_UNEXPECTED (
"\a\t"
10191 "signal (%d, 0x%02X):%s%s "
10193 "С помощью функции signal() вы можете сами обработать эту ошибку.",
10194 sig, (
unsigned) fpe, sSig, sFPE,
10195 ((_txDumpSE[1] ==
'\n')?
"" :
"\n\n"), _txDumpSE + 1);
10200 void _txOnTerminate()
10206 $1
static int terminating = 0;
10207 if (terminating++) {$ _TX_UNEXPECTED (
"\a" "std::terminate() вызвана рекурсивно.");
return; }
10210 {$ _txDumpExceptionCPP (_txDumpSE + 1,
sizeof (_txDumpSE) - 2); }
10212 $ _TX_UNEXPECTED (
"\t\a"
10213 "std::terminate(): Неперехваченное исключение в функции main() или в деструкторе, "
10214 "или другая фатальная ошибка C++. "
10216 "Используйте try/catch блоки, перехватывайте catch (...), проверяйте вызовы виртуальных функций, "
10217 "разбирайтесь, в чем дело.\n\n"
10218 "С помощью std::set_terminate() вы можете сами обработать эту ошибку." + !*_txDumpSE,
10224 void _txOnUnexpected()
10228 $1
if (!*_txDumpSE)
10229 {$ _txDumpExceptionCPP (_txDumpSE + 1,
sizeof (_txDumpSE) - 2); }
10231 $ _TX_UNEXPECTED (
"std::unexpected(): Необработанное исключение.\n\n"
10232 "Проверьте свои catch-блоки. Перехватите catch (...). Если вы (зря) используете "
10233 "спецификацию исключений для функций, проверьте, не нарушена ли она."
10235 "С помощью catch (...) в main() вы можете сами обработать эту ошибку.",
10241 int _txOnMatherr (_exception* exc)
10247 const char* sType =
"Неизвестный тип исключения";
10249 #if !defined (__CYGWIN__)
10251 #define GET_DESCR_(code, descr) case (code): {$ sType = "(" #code "): " descr; break; }
10253 $
switch (exc->type)
10255 GET_DESCR_ (_DOMAIN,
"Нарушение области определения");
10256 GET_DESCR_ (_SING,
"Сингулярность аргумента );
GET_DESCR_ (_PLOSS, "Частичная потеря значимости");
GET_DESCR_ (_TLOSS, "Полная потеря значимости");
GET_DESCR_ (_OVERFLOW, "Результат слишком большой");
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si");
10257 GET_DESCR_ (_PLOSS,
"Частичная потеря значимости");
10258 GET_DESCR_ (_TLOSS,
"Полная потеря значимости");
10259 GET_DESCR_ (_OVERFLOW,
"Результат слишком большой );
GET_DESCR_ (_UNDERFLOW, "Результат слишком маленький");
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si");
10260 GET_DESCR_ (_UNDERFLOW,
"Результат слишком маленький );
default: break;
}
#undef GET_DESCR_
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
"С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
#else
$ _TX_UNEXPECTED ("_matherr(): Математическая ошибка: %s.", sType);
#endif
return 0;
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnNewHandlerAnsi()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$1
$ _TX_UNEXPECTED ("operator new: Ошибка выделения памяти.\n\n"
"С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.");
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityErrorAnsi (const char* msg, void* ptr, int code)
{
txOutputDebugPrintf ("%s - WARNING: %s (%s, 0x%p, %d) called\n", _TX_VERSION, __func__, msg, ptr, code);
$1 if (code)
{$ errno = code; }
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
"С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
"и постараться не выходить за границы массивов.",
code, msg, (char*) ptr, ptr);
}
//-----------------------------------------------------------------------------------------------------------------
inline int tx_glGetError (int setError /*= INT_MIN*/)
{
$1 return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ MSC Runtime check hooks
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
//-----------------------------------------------------------------------------------------------------------------
int _txOnNewHandler (size_t size)
{
txOutputDebugPrintf ("%s - WARNING: %s (0x%p) called\n", _TX_VERSION, __func__, (void*)(uintptr_t) size);
$5
$ _TX_UNEXPECTED ("operator new: Ошибка выделения %llu байт памяти.\n\n"
"С помощью _set_new_handler() вы можете сами обработать эту ошибку "
"и где-нибудь найти недостающую память.", (unsigned long long) size);
$ throw std::bad_alloc();
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnSecurityError (int code, void* addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, 0x%p) called\n", _TX_VERSION, __func__, code, addr);
$5
$ _TX_UNEXPECTED ("\a"
"Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
"С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
"и более торжественно завершить программу. Ставьте же ассерты.", code);
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnPureCall()
{
txOutputDebugPrintf ("%s - WARNING: %s() called\n", _TX_VERSION, __func__);
$5
$ _TX_UNEXPECTED ("\a"
"Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
"или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
"С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
"и проверить свое знание С++ :)");
}
//-----------------------------------------------------------------------------------------------------------------
void _txOnInvalidParam (const wchar_t* wExpr, const wchar_t* wFunc, const wchar_t* wFile, unsigned int line, uintptr_t addr)
{
txOutputDebugPrintf ("%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n", _TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
$5 assert (wExpr);
assert (wFunc);
assert (wFile);
char expr [_TX_BUFSIZE/2] = "[Unknowm expr]",
func [_TX_BUFSIZE/2] = "[Unknowm func]",
file [MAX_PATH] = "[Unknowm file]";
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wExpr, -1, expr, sizeof (expr) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFunc, -1, func, sizeof (func) - 1, NULL, NULL);
$ WideCharToMultiByte (_TX_CODEPAGE, 0, wFile, -1, file, sizeof (file) - 1, NULL, NULL);
$$ _txError (file, (int) line, func, 0, "\a"
"В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
"С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_CLANG_VER) && !defined (_MSC_VER)
void _txLibCppDebugFunction (std::__libcpp_debug_info const& info)
{
$5 assert (&info);
$$ _txError (info.__file_, info.__line_, NULL, 0, "\a"
"Оказалось неверно, что %s (%s). Не надо так.\n\n"
"С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
#pragma runtime_checks ("", off)
int _txOnRTCFailure (int type, const char* file, int line, const char* module, const char* format, ...)
{
txOutputDebugPrintf ("%s - WARNING: %s (%d, %s, %d, %s, %s) called\n", _TX_VERSION, __func__, type, file, line, module, format);
$5 static long running = 0;
$ while (InterlockedExchange (&running, 1)) Sleep (0);
$ assert (format);
// Disable all RTC failures
$ int nErrors = _RTC_NumErrors();
$ int* errors = NULL;
$ try { errors = (int*) _alloca (nErrors * sizeof (*errors)); } catch (...) {;}
$ int err = 0;
$ for (int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
$ char text [_TX_BUFSIZE] = "";
$ va_list arg; va_start (arg, format);
$ _tx_vsnprintf_s (text, sizeof (text) - 1, format, arg); // Get message from the vararg list
$ auto error = (_RTC_ErrorNumber) va_arg (arg, int /*_RTC_ErrorNumber*/); // Get the RTC error number
$ va_end (arg);
$ const char* sType = "type";
$ switch (type)
{
case _CRT_ERROR: $ sType = "ошибка"; break;
case _CRT_ASSERT: $ sType = "логическая ошибка"; break;
case _CRT_WARN: $ sType = "возможная ошибка"; break;
default: $ break;
}
$ const char* sError = _RTC_GetErrDesc (error);
$$ _txError (file, line, NULL, 0, "\a"
"Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
// The code below will be never executed until the error above will stay fatal:
// Restore the RTC error types
#if defined (_MSC_VER)
#pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ for (int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
#if defined (_MSC_VER)
#pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
#endif
$ InterlockedExchange (&running, 0);
$ return 1;
}
#pragma runtime_checks ("", restore)
//-----------------------------------------------------------------------------------------------------------------
int _txOnAllocHook (int type, void* data, size_t size, int use, long request, const unsigned char* file, int line)
{
#if (_TX_ALLOW_TRACE +0 >= 4)
static _tx_thread int recursive = 0;
if (recursive) return true;
recursive++;
#if (_TX_ALLOW_TRACE +0 <= 10)
if (!size) return true;
#endif
#define GET_DESCR_(str, type) case (type): { str = #type; break; }
const char* sType = "Unknown type";
const char* sUse = "Unknown use";
switch (_BLOCK_TYPE (type))
{
GET_DESCR_ (sType, _HOOK_ALLOC);
GET_DESCR_ (sType, _HOOK_REALLOC);
GET_DESCR_ (sType, _HOOK_FREE);
default: break;
}
switch (use)
{
GET_DESCR_ (sUse, _NORMAL_BLOCK);
GET_DESCR_ (sUse, _CRT_BLOCK);
GET_DESCR_ (sUse, _CLIENT_BLOCK);
GET_DESCR_ (sUse, _FREE_BLOCK);
GET_DESCR_ (sUse, _IGNORE_BLOCK);
default: break;
}
#undef GET_DESCR_
_txTrace ((const char*) file, line, NULL, "%*s"
"_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
2 * _txLoc::Cur.inTX, "",
type, 13, sType, _BLOCK_SUBTYPE (type), data, (void*) size, use, 13, sUse, request);
recursive--;
#else
UNREFERENCED_PARAMETER (type);
UNREFERENCED_PARAMETER (data);
UNREFERENCED_PARAMETER (size);
UNREFERENCED_PARAMETER (use);
UNREFERENCED_PARAMETER (request);
UNREFERENCED_PARAMETER (file);
UNREFERENCED_PARAMETER (line);
#endif
return true;
}
//-----------------------------------------------------------------------------------------------------------------
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ SEH staff
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txVectoredExceptionHandler");
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
{
int inTX = _txLoc::Cur.inTX++;
long ret = _txOnExceptionSEH (exc, "_txUnhandledExceptionFilter");
if (_txPrevUEFilter)
{
if (_txSetJmp())
{
int inTX2 = _txLoc::Cur.inTX++;
ret = _txPrevUEFilter (exc);
_txLoc::Cur.inTX = inTX2;
}
else
{
$6 _txClearJmp();
_TX_UNEXPECTED ("\t\a" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
"Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
_txDumpSE + 1);
}
}
_txLoc::Cur.inTX = inTX;
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
{
$6 _txPrevUEFilter = filter;
return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
}
//-----------------------------------------------------------------------------------------------------------------
long _txOnExceptionSEH (EXCEPTION_POINTERS* exc, const char func[])
{
assert (exc); if (!exc) {$ return EXCEPTION_CONTINUE_SEARCH; }
assert (exc->ExceptionRecord);
assert (func);
assert (func[3] == 'V' || func[3] == 'U');
bool unhExc = (func[3] == 'U');
DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
if (code == DBG_PRINTEXCEPTION_C ||
code == DBG_PRINTEXCEPTION_WIDE_C ||
code == DBG_THREAD_NAME ||
(code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
(code == RPC_S_CALL_CANCELLED && !unhExc) ||
(code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
return EXCEPTION_CONTINUE_SEARCH;
_txSENumber++;
if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
OutputDebugString ("\n");
txOutputDebugPrintf ("%s - WARNING: #%ld: %s (code 0x%08lX, addr 0x%p) called\n",
_TX_VERSION, _txSENumber, func, (unsigned long) code, addr);
$6 if (*(unsigned long long*) _txDumpExceptionObjJmp)
{
$ longjmp (_txDumpExceptionObjJmp, 1);
}
tx_fpreset();
#if defined (_MSC_VER)
if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
#endif
$ bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
$ if (primaryException && exc)
{
$ unsigned err = GetLastError();
$ const char* stackTrace = _txCaptureStackBackTrace (0, true, exc->ContextRecord);
$ _txDumpExceptionSEH (_txDumpSE, (intptr_t) sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
$ _tx_snprintf_s (_txTraceSE, (intptr_t) sizeof (_txTraceSE) - 1, "%s", stackTrace);
$ static _tx_thread DWORD prevCode = 0;
$ static _tx_thread void* prevAddr = NULL;
$ if (code != prevCode && addr != prevAddr &&
!strstr (_txDumpSE, "Объект исключения C++:"))
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ SetLastError (err);
$ _TX_UNEXPECTED ("\v\b\t" "%s", _txDumpSE + 1);
$ prevCode = code;
$ prevAddr = addr;
}
$ SetLastError (err);
}
$ if (_txDumpSE[0] == '\a' ||
_txSENumber >= _TX_EXCEPTIONS_LIMIT+0 ||
_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{
#if !defined (_TX_NO_MINIDUMP)
$ _txCreateMiniDump (exc);
#endif
$ _TX_UNEXPECTED ("\a\t" "%s"
"С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
_txDumpSE + 1);
}
$ return EXCEPTION_CONTINUE_SEARCH;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionSEH (char what[], intptr_t size, const EXCEPTION_RECORD* exc, const char func[])
{
$6 assert (what);
$ assert (size >= 0);
assert (exc); if (!exc) {$ return 0; }
$ assert (func);
$ unsigned code = exc->ExceptionCode;
$ void* addr = exc->ExceptionAddress;
$ unsigned params = exc->NumberParameters;
$ const ULONG_PTR* info = exc->ExceptionInformation;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ char* s = what;
#define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
$ const char* sCode = NULL;
$ const char* sDescr = NULL;
#define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
$ switch (code)
{
GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION, " " "Нарушение доступа к памяти.")
GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si");
10266 $ _TX_UNEXPECTED (
"_matherr(): Математическая ошибка %d %s в функции %s (%lg, [%lg]). Она вернет значение %lg.\n\n"
10267 "С помощью __setusermatherr() вы можете сами обработать эту ошибку.",
10268 exc->type, sType, exc->name, exc->arg1, exc->arg2, exc->retval);
10271 $ _TX_UNEXPECTED (
"_matherr(): Математическая ошибка: %s.", sType);
10280 void _txOnNewHandlerAnsi()
10284 $ _TX_UNEXPECTED (
"operator new: Ошибка выделения памяти.\n\n"
10285 "С помощью std::set_new_handler() вы можете сами обработать эту ошибку "
10286 "и где-нибудь найти недостающую память.");
10288 $
throw std::bad_alloc();
10293 void _txOnSecurityErrorAnsi (
const char* msg,
void* ptr,
int code)
10300 $ _TX_UNEXPECTED (
"\a"
10301 "Ошибка переполнения буфера %d: %s в %.20s (0x%p). Ставьте ассерты!\n\n"
10302 "С помощью std::set_constraint_handler_s() вы можете сами обработать эту ошибку "
10303 "и постараться не выходить за границы массивов.",
10304 code, msg, (
char*) ptr, ptr);
10309 inline int tx_glGetError (
int setError )
10311 $1
return _txOGLError = (setError == INT_MIN)? Win32::glGetError() : setError;
10321 #if defined (_MSC_VER)
10325 int _txOnNewHandler (
size_t size)
10329 $ _TX_UNEXPECTED (
"operator new: Ошибка выделения %llu байт памяти.\n\n"
10330 "С помощью _set_new_handler() вы можете сами обработать эту ошибку "
10331 "и где-нибудь найти недостающую память.", (
unsigned long long) size);
10333 $
throw std::bad_alloc();
10338 void _txOnSecurityError (
int code,
void* addr)
10342 $ _TX_UNEXPECTED (
"\a"
10343 "Ошибка переполнения буфера %d (_SECERR_BUFFER_OVERRUN). Ставьте ассерты!\n\n"
10344 "С помощью _set_security_error_handler() вы можете сами обработать эту ошибку "
10345 "и более торжественно завершить программу. Ставьте же ассерты.", code);
10350 void _txOnPureCall()
10354 $ _TX_UNEXPECTED (
"\a"
10355 "Вызвана чисто виртуальная функция. Такое бывает, например, в конструкторах "
10356 "или деструкторах базовых классов - не вызывайте там таких функций.\n\n"
10357 "С помощью _set_purecall_handler() вы можете сами обработать эту ошибку "
10358 "и проверить свое знание С++ :)");
10363 void _txOnInvalidParam (
const wchar_t* wExpr,
const wchar_t* wFunc,
const wchar_t* wFile,
unsigned int line, uintptr_t addr)
10365 txOutputDebugPrintf (
"%s - WARNING: %s (%S, %S, %S, %d, 0x%p) called\n",
_TX_VERSION, __func__, wExpr, wFunc, wFile, line, addr);
10373 file [MAX_PATH] =
"[Unknowm file]";
10375 $ WideCharToMultiByte (
_TX_CODEPAGE, 0, wExpr, -1, expr,
sizeof (expr) - 1, NULL, NULL);
10376 $ WideCharToMultiByte (
_TX_CODEPAGE, 0, wFunc, -1, func,
sizeof (func) - 1, NULL, NULL);
10377 $ WideCharToMultiByte (
_TX_CODEPAGE, 0, wFile, -1, file,
sizeof (file) - 1, NULL, NULL);
10379 $$ _txError (file, (
int) line, func, 0,
"\a"
10380 "В функцию %s передан неверный параметр: неверно, что %s. Не надо так.\n\n"
10381 "С помощью _set_invalid_parameter_handler() вы можете сами обработать эту ошибку.", func, expr);
10386 #if defined (_CLANG_VER) && !defined (_MSC_VER)
10388 void _txLibCppDebugFunction (std::__libcpp_debug_info
const& info)
10392 $$ _txError (info.__file_, info.__line_, NULL, 0,
"\a"
10393 "Оказалось неверно, что %s (%s). Не надо так.\n\n"
10394 "С помощью std::__libcpp_debug_function вы можете сами обработать эту ошибку.", info.__pred_, info.__msg_);
10401 #pragma runtime_checks ("", off)
10403 int _txOnRTCFailure (
int type,
const char* file,
int line,
const char* module,
const char* format, ...)
10405 txOutputDebugPrintf (
"%s - WARNING: %s (%d, %s, %d, %s, %s) called\n",
_TX_VERSION, __func__, type, file, line, module, format);
10407 $5
static long running = 0;
10408 $
while (InterlockedExchange (&running, 1)) Sleep (0);
10414 $
int nErrors = _RTC_NumErrors();
10415 $
int* errors = NULL;
10416 $
try { errors = (
int*) _alloca (nErrors *
sizeof (*errors)); }
catch (...) {;}
10419 $
for (
int i = 0; i < nErrors; i++) *(errors? &errors[i] : &err) = _RTC_SetErrorType ((_RTC_ErrorNumber) i, _RTC_ERRTYPE_IGNORE);
10423 $ va_list arg; va_start (arg, format);
10424 $ _tx_vsnprintf_s (text,
sizeof (text) - 1, format, arg);
10425 $
auto error = (_RTC_ErrorNumber) va_arg (arg,
int );
10428 $
const char* sType =
"type";
10432 case _CRT_ERROR: $ sType =
"ошибка";
break;
10433 case _CRT_ASSERT: $ sType =
"логическая ошибка";
break;
10434 case _CRT_WARN: $ sType =
"возможная ошибка";
break;
10438 $
const char* sError = _RTC_GetErrDesc (error);
10440 $$ _txError (file, line, NULL, 0,
"\a"
10441 "Сбой проверки выполнения машинного кода: %s %d (%s): %s в модуле %s.", sType, error, sError, text, module);
10447 #if defined (_MSC_VER)
10448 #pragma warning (disable: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
10451 $
for (
int i = 0; i < nErrors; i++) _RTC_SetErrorType ((_RTC_ErrorNumber) i, (errors? errors[i] : _CRT_ERROR));
10453 #if defined (_MSC_VER)
10454 #pragma warning (default: 6385) // Reading invalid data from 'errors': the readable size is 'n' bytes, but 'm' bytes may be read.
10457 $ InterlockedExchange (&running, 0);
10461 #pragma runtime_checks ("", restore)
10465 int _txOnAllocHook (
int type,
void* data,
size_t size,
int use,
long request,
const unsigned char* file,
int line)
10467 #if (_TX_ALLOW_TRACE +0 >= 4)
10469 static _tx_thread
int recursive = 0;
10470 if (recursive)
return true;
10473 #if (_TX_ALLOW_TRACE +0 <= 10)
10474 if (!size)
return true;
10477 #define GET_DESCR_(str, type) case (type): { str = #type; break; }
10479 const char* sType =
"Unknown type";
10480 const char* sUse =
"Unknown use";
10482 switch (_BLOCK_TYPE (type))
10484 GET_DESCR_ (sType, _HOOK_ALLOC);
10485 GET_DESCR_ (sType, _HOOK_REALLOC);
10486 GET_DESCR_ (sType, _HOOK_FREE);
10492 GET_DESCR_ (sUse, _NORMAL_BLOCK);
10493 GET_DESCR_ (sUse, _CRT_BLOCK);
10494 GET_DESCR_ (sUse, _CLIENT_BLOCK);
10495 GET_DESCR_ (sUse, _FREE_BLOCK);
10496 GET_DESCR_ (sUse, _IGNORE_BLOCK);
10502 _txTrace ((
const char*) file, line, NULL,
"%*s"
10503 "_txOnAllocHook (type = 0x%02X (%-*s), subtype =0x%X, data = 0x%p, size = 0x%p, use = 0x%02X (%-*s), request = %ld)",
10504 2 * _txLoc::Cur.inTX,
"",
10505 type, 13, sType, _BLOCK_SUBTYPE (type), data, (
void*) size, use, 13, sUse, request);
10511 UNREFERENCED_PARAMETER (type);
10512 UNREFERENCED_PARAMETER (data);
10513 UNREFERENCED_PARAMETER (size);
10514 UNREFERENCED_PARAMETER (use);
10515 UNREFERENCED_PARAMETER (request);
10516 UNREFERENCED_PARAMETER (file);
10517 UNREFERENCED_PARAMETER (line);
10535 long WINAPI _txVectoredExceptionHandler (EXCEPTION_POINTERS* exc)
10537 int inTX = _txLoc::Cur.inTX++;
10539 long ret = _txOnExceptionSEH (exc,
"_txVectoredExceptionHandler");
10541 _txLoc::Cur.inTX = inTX;
10547 long WINAPI _txUnhandledExceptionFilter (EXCEPTION_POINTERS* exc)
10549 int inTX = _txLoc::Cur.inTX++;
10551 long ret = _txOnExceptionSEH (exc,
"_txUnhandledExceptionFilter");
10553 if (_txPrevUEFilter)
10557 int inTX2 = _txLoc::Cur.inTX++;
10559 ret = _txPrevUEFilter (exc);
10561 _txLoc::Cur.inTX = inTX2;
10567 _TX_UNEXPECTED (
"\t\a" "%s"
10568 "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.\n\n"
10569 "Дополнительно: Сбой вызова стандартного обработчика неперехваченнных исключений SEH." + !*_txDumpSE,
10574 _txLoc::Cur.inTX = inTX;
10580 LPTOP_LEVEL_EXCEPTION_FILTER WINAPI _txOnSetUnhandledExceptionFilter (LPTOP_LEVEL_EXCEPTION_FILTER filter)
10582 $6 _txPrevUEFilter = filter;
10584 return (LPTOP_LEVEL_EXCEPTION_FILTER) _txUnhandledExceptionFilter;
10589 long _txOnExceptionSEH (EXCEPTION_POINTERS* exc,
const char func[])
10591 assert (exc);
if (!exc) {$
return EXCEPTION_CONTINUE_SEARCH; }
10593 assert (exc->ExceptionRecord);
10596 assert (func[3] ==
'V' || func[3] ==
'U');
10598 bool unhExc = (func[3] ==
'U');
10599 DWORD code = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionCode : 0;
10600 void* addr = (exc && exc->ExceptionRecord)? exc->ExceptionRecord->ExceptionAddress : NULL;
10602 if (code == DBG_PRINTEXCEPTION_C ||
10603 code == DBG_PRINTEXCEPTION_WIDE_C ||
10604 code == DBG_THREAD_NAME ||
10605 (code == RPC_S_SERVER_UNAVAILABLE && !unhExc) ||
10606 (code == RPC_S_CALL_CANCELLED && !unhExc) ||
10607 (code == EXCEPTION_BREAKPOINT && IsDebuggerPresent()))
10608 return EXCEPTION_CONTINUE_SEARCH;
10611 if (HIBYTE (HIWORD (code)) == 0xC0) _txSEFatalNumber++;
10613 OutputDebugString (
"\n");
10615 _TX_VERSION, _txSENumber, func, (
unsigned long) code, addr);
10617 $6
if (*(
unsigned long long*) _txDumpExceptionObjJmp)
10619 $ longjmp (_txDumpExceptionObjJmp, 1);
10624 #if defined (_MSC_VER)
10625 if (code == EXCEPTION_STACK_OVERFLOW) {$ _resetstkoflw(); }
10628 $
bool primaryException = !(func && exc) || !((unhExc && *_txDumpSE) || _TX_MSC__CXX_DETECT_RETHROW (exc->ExceptionRecord));
10630 $
if (primaryException && exc)
10632 $
unsigned err = GetLastError();
10634 $
const char* stackTrace = _txCaptureStackBackTrace (0,
true, exc->ContextRecord);
10636 $ _txDumpExceptionSEH (_txDumpSE, (intptr_t)
sizeof (_txDumpSE) - 1, exc->ExceptionRecord, func);
10637 $ _tx_snprintf_s (_txTraceSE, (intptr_t)
sizeof (_txTraceSE) - 1,
"%s", stackTrace);
10639 $
static _tx_thread DWORD prevCode = 0;
10640 $
static _tx_thread
void* prevAddr = NULL;
10642 $
if (code != prevCode && addr != prevAddr &&
10643 !strstr (_txDumpSE,
"Объект исключения C++:"))
10645 #if !defined (_TX_NO_MINIDUMP)
10646 $ _txCreateMiniDump (exc);
10649 $ SetLastError (err);
10650 $ _TX_UNEXPECTED (
"\v\b\t" "%s", _txDumpSE + 1);
10656 $ SetLastError (err);
10659 $
if (_txDumpSE[0] ==
'\a' ||
10663 #if !defined (_TX_NO_MINIDUMP)
10664 $ _txCreateMiniDump (exc);
10667 $ _TX_UNEXPECTED (
"\a\t" "%s"
10668 "С помощью функции _set_se_translator() вы можете сами обработать эту ошибку.",
10672 $
return EXCEPTION_CONTINUE_SEARCH;
10677 intptr_t _txDumpExceptionSEH (
char what[], intptr_t size,
const EXCEPTION_RECORD* exc,
const char func[])
10681 assert (exc);
if (!exc) {$
return 0; }
10684 $
unsigned code = exc->ExceptionCode;
10685 $
void* addr = exc->ExceptionAddress;
10686 $
unsigned params = exc->NumberParameters;
10687 $
const ULONG_PTR* info = exc->ExceptionInformation;
10688 $
void*
object = (params >= 2)? (
void*) info[1] : NULL;
10692 #define PRINT_(...) s += _tx_snprintf_s (s, size-2 - (s-what), ##__VA_ARGS__)
10694 $
const char* sCode = NULL;
10695 $
const char* sDescr = NULL;
10697 #define GET_DESCR_(code, descr) case ((unsigned long) (code)): {$ sCode = #code; sDescr = descr; break; }
10701 GET_DESCR_ (EXCEPTION_ACCESS_VIOLATION,
" " "Нарушение доступа к памяти.")
10702 GET_DESCR_ (EXCEPTION_ILLEGAL_INSTRUCTION, " " "Недопустимая операция.")
10703 GET_DESCR_ (EXCEPTION_PRIV_INSTRUCTION, " " "Привилегированная операция.")
10704 GET_DESCR_ (EXCEPTION_ARRAY_BOUNDS_EXCEEDED, "\a" "Выход за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si за границы массива. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si массива Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si. Ставьте ассерты!")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si ассерты ")
GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si!")
10705 GET_DESCR_ (EXCEPTION_BREAKPOINT, "\a" "Достигнута точка останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si останова. Удачи в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si в отладке!")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si отладке ")
GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si!")
10706 GET_DESCR_ (EXCEPTION_DATATYPE_MISALIGNMENT, "\a" "Нарушение выравнивания данных.")
GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si выравнивания данных.")
10707 GET_DESCR_ (EXCEPTION_INVALID_DISPOSITION, "\a" "Обработчик исключения возвратил неверное значение.")
GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si неверное значение.")
10708 GET_DESCR_ (EXCEPTION_IN_PAGE_ERROR, "\a" "Невозможно загрузить нужную страницу памяти.")
GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si нужную страницу памяти.")
10709 GET_DESCR_ (EXCEPTION_NONCONTINUABLE_EXCEPTION, "\a" "Продолжение выполнения невозможно.")
GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si выполнения невозможно.")
10710 GET_DESCR_ (EXCEPTION_SINGLE_STEP, "\a" "Выполнена инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si инструкция машинного кода. Одна штука.")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si кода. Одна штука ")
GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю-ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10711 GET_DESCR_ (EXCEPTION_STACK_OVERFLOW, "\a" "Ю ху! Переполнение стека!")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si-ху! Переполнение стека ")
GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si!")
10712 GET_DESCR_ (EXCEPTION_GUARD_PAGE, "\a" "Попытка доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si доступа к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si к защищенной странице памяти.")
GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si защищенной странице памяти.")
10713 GET_DESCR_ (EXCEPTION_INVALID_HANDLE, "\a" "Неверный или уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si уже закрытый дескриптор.")
GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si закрытый дескриптор.")
10714 GET_DESCR_ (STATUS_POSSIBLE_DEADLOCK, "\a" "Возможно, взаимная блокировка ресурсов.")
10716 GET_DESCR_ (EXCEPTION_FLT_STACK_CHECK, "\a" "Ошибка стека при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10717 GET_DESCR_ (EXCEPTION_FLT_DENORMAL_OPERAND, " " "Денормализация числа с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10718 GET_DESCR_ (EXCEPTION_FLT_DIVIDE_BY_ZERO, " " "Деление на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si на ноль при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10719 GET_DESCR_ (EXCEPTION_FLT_INEXACT_RESULT, " " "Неточный результат при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10720 GET_DESCR_ (EXCEPTION_FLT_INVALID_OPERATION, " " "Недопустимая операция с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10721 GET_DESCR_ (EXCEPTION_FLT_OVERFLOW, " " "Переполнение при операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si операции с плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si плавающей точкой.")
GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10722 GET_DESCR_ (EXCEPTION_FLT_UNDERFLOW, " " "Потеря значимости при операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si операции с плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si плавающей точкой.")
GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10723 GET_DESCR_ (STATUS_FLOAT_MULTIPLE_FAULTS, " " "Множественные ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si ошибки с плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si плавающей точкой.")
GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si точкой.")
10725 GET_DESCR_ (EXCEPTION_INT_DIVIDE_BY_ZERO, "\a" "Целочисленное деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si деление на ноль.")
GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si на ноль.")
10726 GET_DESCR_ (EXCEPTION_INT_OVERFLOW, "\a" "Переполнение при целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si целочисленной операции.")
GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si операции.")
10728 GET_DESCR_ (EXCEPTION_CLR_FAILURE, "\a" "Сбой среды исполнения (CLR).")
GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si исполнения (CLR).")
10729 GET_DESCR_ (STATUS_STACK_BUFFER_OVERRUN, "\a" "Переполнение стекового буфера!")
GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si буфера!")
10730 GET_DESCR_ (STATUS_ASSERTION_FAILURE, "\a" "Сработал
assert. На этот раз из ядра.")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si из ядра ")
GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10731 GET_DESCR_ (STATUS_WX86_BREAKPOINT, "\a" "Точка останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si останова подсистемы эмуляции x86.")
GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si x86.")
10732 GET_DESCR_ (RPC_S_UNKNOWN_IF, "\a" "Неизвестный интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si интерфейс удаленного вызова процедур (RPC).")
GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si удаленного вызова процедур (RPC).")
10733 GET_DESCR_ (RPC_S_SERVER_UNAVAILABLE, "\a" "Сервер удаленного вызова процедур (RPC) недоступен.")
10734 GET_DESCR_ (DBG_TERMINATE_THREAD, "\a" "Отладчик завершил поток сознания.")
GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс.")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si сознания.")
10735 GET_DESCR_ (DBG_TERMINATE_PROCESS, "\a" "Отладчик завершил процесс ")
GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10736 GET_DESCR_ (DBG_CONTROL_C, "\a" "Отладчик получил сигнал прерывания Control+C.")
GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si сигнал прерывания Control+C.")
10737 GET_DESCR_ (DBG_CONTROL_BREAK, "\a" "Отладчик получил сигнал прерывания Control+Break.")
GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si сигнал прерывания Control+Break.")
10738 GET_DESCR_ (DBG_THREAD_NAME, " " "Отладчик получил указание дать потоку имя.")
GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si указание дать потоку имя.")
10739 GET_DESCR_ (DBG_PRINTEXCEPTION_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si исключение по CTRL+C (OutputDebugStringA).")
GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si по CTRL+C (OutputDebugStringA).")
10740 GET_DESCR_ (DBG_PRINTEXCEPTION_WIDE_C, " " "Отладчик вывел исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si исключение по CTRL+C (OutputDebugStringW).")
GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si по CTRL+C (OutputDebugStringW).")
10742 GET_DESCR_ (EXCEPTION_CPP_MSC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si С +, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si оператором throw.")
10743 GET_DESCR_ (EXCEPTION_CPP_GCC, " " "Исключение С++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si С +, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si++, вызванное оператором throw.")
GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si оператором throw.")
10744 GET_DESCR_ (EXCEPTION_CPP_GCC_UNWIND, " " "Исключение С++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si С +, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si++, вызванное во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si во время раскрутки стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si стека (rethrow?).")
GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si (rethrow?).")
10745 GET_DESCR_ (EXCEPTION_CPP_GCC_FORCED, " " "Исключение С++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si С +, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si++, вызванное нарушением магии.")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si нарушением магии ")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si.")
10746 GET_DESCR_ (EXCEPTION_CPP_BORLAND_BUILDER, "\a" "Это скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si скомпилилось под Билдер? O_O")
GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si Билдер? O_O")
10747 GET_DESCR_ (EXCEPTION_CPP_BORLAND_DELPHI, "\a" "Это же С++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si же С +. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si++. Как это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si это вышло?")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si вышло ")
default: $ break;
}
#undef GET_DESCR_
$ if (sDescr)
{
$ PRINT_ ("%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
}
else
{
$ PRINT_ ("\a#%ld: ", _txSENumber);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ ("\r\r");
}
$ PRINT_ (" (0x%X) при выполнении кода по адресу", code);
$ PRINT_ ((addr? " 0x%p" : " NULL"), addr);
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
$ if (sym && *sym->Name) PRINT_ (" в функции %s()", sym->Name);
$ if (line && line->FileName && *line->FileName) PRINT_ (" в файле %s на строке %u", line->FileName, (unsigned) line->LineNumber);
$ if (code == EXCEPTION_ACCESS_VIOLATION ||
code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (". Попытка ");
$ unsigned long op = 0xBADC0DE;
$ const char* sOp = "(действие не указано)";
$ if (params >= 1)
{
$ switch (op = (unsigned long) info[0])
{
case 0: $ sOp = "прочесть данные"; break;
case 1: $ sOp = "записать данные"; break;
case 8: $ sOp = "исполнить код"; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si?")
10756 $ PRINT_ (
"%s\n\n" "#%ld: Исключение %s", sDescr, _txSENumber, sCode);
10760 $ PRINT_ (
"\a#%ld: ", _txSENumber);
10761 $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
10762 GetModuleHandle (
"NTDLL.DLL"), code, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
10763 s, (DWORD) (size - (s-what)), NULL) - 2;
10767 $ PRINT_ (
" (0x%X) при выполнении кода по адресу", code);
10768 $ PRINT_ ((addr?
" 0x%p" :
" NULL"), addr);
10770 $ Win32::SYMBOL_INFO* sym = NULL;
10771 $ Win32::IMAGEHLP_LINE64* line = NULL;
10773 if (addr) {$ _txSymGetFromAddr (addr, &sym, &line); }
10775 $
if (sym && *sym->Name) PRINT_ (
" в функции %s()", sym->Name);
10776 $
if (line && line->FileName && *line->FileName) PRINT_ (
" в файле %s на строке %u", line->FileName, (
unsigned) line->LineNumber);
10778 $
if (code == EXCEPTION_ACCESS_VIOLATION ||
10779 code == EXCEPTION_IN_PAGE_ERROR)
10781 $ PRINT_ (
". Попытка ");
10783 $
unsigned long op = 0xBADC0DE;
10784 $
const char* sOp =
"(действие не указано)";
10788 $
switch (op = (
unsigned long) info[0])
10790 case 0: $ sOp =
"прочесть данные";
break;
10791 case 1: $ sOp =
"записать данные";
break;
10792 case 8: $ sOp =
"исполнить код ; break;
default: $ sOp = "совершить операцию 0x%lX"; break;
}
}
$ PRINT_ (sOp, op);
$ if (params >= 2) {$ PRINT_ ((object? " по адресу 0x%p" : " по адресу NULL"), object); }
else {$ PRINT_ (" (адрес не указан)"); }
$ if (code == EXCEPTION_IN_PAGE_ERROR)
{
$ PRINT_ (", ошибка ввода-вывода:");
$ if (params >= 3)
{
$ unsigned long ntstatus = (unsigned long) info[2];
$ PRINT_ (" 0x%lX (", ntstatus);
$ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
GetModuleHandle ("NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (size - (s-what)), NULL) - 2;
$ PRINT_ (")");
}
else
{$ PRINT_ (" (не указана)"); }
}
}
$ HMODULE module = NULL;
$ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (const char*) addr, &module));
$ if (module)
{
$ static char sModule [MAX_PATH] = "";
$ int ok = GetModuleFileName (module, sModule, sizeof (sModule));
$ char* ext = (ok? strrchr (sModule, '.') : NULL);
$ if (ext) _strlwr_s (ext, sizeof (sModule) - 1 - (ext - sModule));
if (ok) {$ PRINT_ (" в модуле %s", sModule); }
else {$ PRINT_ (" в модуле 0x%p", module); }
}
$ PRINT_ (".");
$ if (_txSENumber >= _TX_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).", _TX_EXCEPTIONS_LIMIT+0); }
$ if (_txSEFatalNumber >= _TX_FATAL_EXCEPTIONS_LIMIT+0)
{$ PRINT_ (" Также превышен лимит фатальных исключений _TX_FATAL_EXCEPTIONS_LIMIT (%d).", _TX_FATAL_EXCEPTIONS_LIMIT+0); }
$ PRINT_ (" Спасибо %s(), что сообщил. Люблю его <3", func);
$ if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
{$ PRINT_ ("\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
$ if (exc->ExceptionRecord)
{
$ PRINT_ ("\n\n" "Причина:" "\n\n");
$ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
}
$ if (code == EXCEPTION_CPP_GCC ||
code == EXCEPTION_CPP_GCC_UNWIND ||
code == EXCEPTION_CPP_GCC_FORCED ||
code == EXCEPTION_CPP_MSC)
{
$ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
}
#undef PRINT_
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s-what), "\n\n");
$ return s - what;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionCPP (char what[], intptr_t size,
unsigned code /*= 0*/, unsigned params /*= 0*/, const ULONG_PTR info[] /*= NULL*/)
{
$6 assert (what);
$ assert (size >= 0);
$ char* s = what;
$ switch (code)
{
#if defined (_GCC_VER)
case EXCEPTION_CPP_GCC:
case EXCEPTION_CPP_GCC_UNWIND:
case EXCEPTION_CPP_GCC_FORCED:
{
// See: [1] http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_exception.cpp
// [2] http://github.com/gcc-mirror/gcc/blob/master/libgcc/unwind-seh.c, lines 51-55 and below
// [3] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_throw.cc, __cxa_throw, line 59 and below
// [4] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/unwind-cxx.h, __cxa_exception, line 58 and below
// and figure above near ABI::__cxa_exception definition in this file
$ const std::type_info* type = NULL;
$ void* object = NULL;
$ if (params >= 1)
{
$ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
$ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
$ type = cxa_exception->exceptionType;
$ object = cxa_exception + 1;
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, 0, type);
}
$ break;
case 0: // Not called within SEH chain
{
// From: [1] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_type.cc
// [2] http://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/vterminate.cc
using namespace abi;
$ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
$ if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1)) // Dependent exception, case B, see pic above
{
$ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
}
$ if (cxa_exception)
{
$ verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
$ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
}
}
$ break;
#elif defined (_MSC_VER)
case EXCEPTION_CPP_MSC:
{
// See [1] http://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273
// [2] http://www.openrce.org/articles/full_view/21
// [3] http://www.openrce.org/articles/full_view/23
// [4] http://yurichev.com/mirrors/RE/Recon-2012-Skochinsky-Compiler-Internals.pdf
$ const std::type_info* type = NULL;
$ void* object = (params >= 2)? (void*) info[1] : NULL;
$ size_t szObj = 0;
$ if (params >= 3 &&
(info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
{
$ auto throwInfo = (const Win32::ThrowInfo*) info[2];
$ if (throwInfo && throwInfo->pCatchableTypeArray)
{
$ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
#define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
$ const Win32::CatchableTypeArray* cArray = RVA_(const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
$ const Win32::CatchableType* cType = RVA_(const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
$ type = RVA_(const std::type_info*, cType->pType);
$ szObj = cType->sizeOrOffset;
#undef RVA_
}
}
$ s += _txDumpExceptionObj (s, size - (s-what), object, szObj, type);
}
break;
case 0: // Not called within SEH chain
// signal() handlers or unexpected()/terminate() are called after Vectored Exception in MSC:
//
// terminate() is called by __scrt_unhandled_exception_filter() in case of MSC exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\utility_desktop.cpp
//
// signal() handlers are called by _seh_filter_exe(), which is called by _mainCRTStartup() in case of exception.
// See C:\Bin\Microsoft Visual Studio 14.0\VC\crt\src\vcruntime\mcrtexe.cpp
// and C:\Bin\Windows Kits\10\Source\10.0.10240.0\ucrt\misc\exception_filter.cpp
// and http://msdn.microsoft.com/en-us/library/ff730818.aspx.
//
// So _txDumpSE information should have been recorded during previous call. Now do nothing.
$ break;
#endif
default:
$ txOutputDebugPrintf ("ERROR: Wrong call to %s: Unknown exception code 0x%08X\n", __TX_FUNCTION__, code);
$ break;
}
$ while (s > what && s[-1] == '\n') s--;
$ if (s > what) s += _tx_snprintf_s (s, size - (s - what), "\n\n");
$ return (s - what);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txDumpExceptionObj (char what[], intptr_t size, void* object, size_t sizeObj, const std::type_info* type)
{
$6 assert (what);
$ assert (size > 0);
$ static char* s = NULL; s = what;
$ static size_t szObj = 0; szObj = sizeObj;
#define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
$ PRINT_ ("\n\n" "Объект исключения C++:");
$ const char* mangledName = (type)? type->name() : NULL;
$ char* typeName = NULL;
$ int err = 1;
#if defined (_GCC_VER)
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
#endif
$ const char* name = (!err && typeName)? typeName : mangledName;
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
{$ name = "std::string"; }
$ if (name &&
(strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
strcmp (name, "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
strcmp (name, "std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
{$ name = "std::string*"; }
if (name) {$ PRINT_ (" %s", name); }
#if defined (_GCC_VER)
$ free (typeName);
#endif
$ err = 0;
$ if (mangledName)
{
if (_txSetJmp())
{
#define PRINT_VAL_(fmt, typ, ...) \
else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
if (false) ;
PRINT_VAL_ ("%s", char*) PRINT_VAL_ ('%c', unsigned char) PRINT_VAL_ (%s, bool, ? "true" : "false")
PRINT_VAL_ ( %d, int) PRINT_VAL_ ( %u, unsigned int) PRINT_VAL_ (%g, float)
PRINT_VAL_ ( %hd, short) PRINT_VAL_ ( %hu, unsigned short) PRINT_VAL_ (%lg, double)
PRINT_VAL_ ( %ld, long) PRINT_VAL_ ( %lu, unsigned long) PRINT_VAL_ ('%c', char)
PRINT_VAL_ ("%s", std::string, .c_str())
else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* ) object))
{
$ PRINT_ (", what(): \"%s\"", e->what());
}
else
{$ err = 1; }
}
else
{$ err = 2; }
}
$ _txClearJmp();
$ if (err && object && szObj)
{
$ const unsigned char* buf = (const unsigned char*) object;
$ if (szObj >= 64) szObj = 64;
$ PRINT_ (", дамп: [");
$ for (size_t i = 0; i < szObj; i++) PRINT_ ("%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] : '.' );
$ PRINT_ ("]");
$ for (size_t i = 0; i < szObj; i++) PRINT_ (" %02X", buf[i]);
$ err = 0;
}
$ if (err)
{$ PRINT_ (" = ???"); }
$ PRINT_ ((object? "%sего адрес 0x%p." : "%sего адрес NULL."), ((typeName || mangledName)? ", " : ""), object);
#undef PRINT_VAL_
#undef PRINT_
$ return s - what;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Stack trace and debug info access
//-----------------------------------------------------------------------------------------------------------------
const char* _txCaptureStackBackTrace (int framesToSkip /*= 0*/, bool readSource /*= true*/,
CONTEXT* context /*= NULL*/, HANDLE thread /*= GetCurrentThread()*/)
{
$6 const int maxFrames = 62; // MS says: < 63
$ static char trace [(MAX_PATH + 1024+1) * maxFrames] = "";
if (framesToSkip == -1) {$ return trace; }
$ static void* capture [maxFrames] = {};
$ int frames = _txStackWalk (framesToSkip + !context, sizearr (capture), capture, NULL, context, thread);
$ memset (trace, 0, sizeof (trace));
$ char* s = trace;
#define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
$ for (int i = 0, n = 0; i < frames; i++)
{
$ void* addr = capture[i];
$ Win32::SYMBOL_INFO* sym = NULL;
$ Win32::IMAGEHLP_LINE64* line = NULL;
$ const char* module = NULL;
$ const char* source = NULL;
$ bool inTX = false;
if (addr) {$ inTX = _txSymGetFromAddr ((char*) addr - 1, &sym, &line, &module); }
if (readSource && !inTX) {$ _txSymGetFromAddr ((void*) 1, NULL, NULL, NULL, &source, 2); }
$ int nl = 0;
$ while (s > trace && s[-1] == '\n') { s--; nl++; }
#if !defined (_TX_FULL_STACKTRACE)
$ if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
{$ continue; }
#endif
$ PRINT_ ("%s#%2d 0x%p", ((n)? ((source || nl)? "\n\n" : "\n") : ""), i, addr);
$ n++;
if (addr == 0) {$ PRINT_ (" [Неверный фрейм]"); break; }
if (addr == (void*) -1) {$ PRINT_ (" [Странный фрейм]"); break; }
if (addr == (void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (" [Мусор от LocalAlloc()]"); break; }
if (module) {$ PRINT_ (" in %s%s", module, ((sym && *sym->Name)? ": " : "")); }
if (sym && *sym->Name) {$ PRINT_ ("%s()", sym->Name); }
if (line && line->FileName) {$ PRINT_ (" at %s", line->FileName); }
if (line && line->LineNumber) {$ PRINT_ (" (%d)", (int) line->LineNumber); }
if (source) {$ PRINT_ (":\n\n" "%s\n", source); }
if (sym && strcmp (sym->Name , "main") == 0) {$ break; }
}
#if defined (_MSC_VER)
#pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
#endif
$ while (s > trace && s[-1] == '\n') s--;
$ *s = 0;
#if defined (_MSC_VER)
#pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
#endif
#undef PRINT_
$ s += _tx_snprintf_s (s, sizeof (trace) - 1 - (s-trace), "");
$ return trace;
}
//-----------------------------------------------------------------------------------------------------------------
// Stack WALKING if the program is DEAD. Dead, Carl!
int _txStackWalk (int framesToSkip, size_t szCapture, void* capture[], unsigned long* /*backTraceHash*/,
CONTEXT* context /*= NULL*/, HANDLE thread /* = GetCurrentThread()*/)
{
$6 namespace MinGW = Win32::MinGW;
$ assert (capture);
$ int cpu = 0;
$ Win32::STACKFRAME64 frame = {};
$ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
$ CONTEXT ctx = {};
$ ctx.ContextFlags |= CONTEXT_FULL;
$ int isWow64 = 0;
$ if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
else {$ return -1; }
$ if (context)
{
$ ctx = *context;
}
else
{
$ assert (Win32::RtlCaptureContext);
$ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
}
#if defined (_WIN64)
$ if (isWow64)
{
$ Win32::WOW64_CONTEXT wow64ctx = {};
$ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
$ if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx))) // In WINE, after EXIT_PROCESS_DEBUG_EVENT
{$ return 0; }
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = wow64ctx.Eip;
$ frame.AddrStack.Offset = wow64ctx.Esp;
$ frame.AddrFrame.Offset = wow64ctx.Ebp;
}
else
{
$ cpu = IMAGE_FILE_MACHINE_AMD64;
$ frame.AddrPC .Offset = ctx.Rip;
$ frame.AddrStack.Offset = ctx.Rbp;
$ frame.AddrFrame.Offset = ctx.Rsp;
}
#else
{
$ cpu = IMAGE_FILE_MACHINE_I386;
$ frame.AddrPC .Offset = ctx.Eip;
$ frame.AddrStack.Offset = ctx.Ebp;
$ frame.AddrFrame.Offset = ctx.Esp;
}
#endif
$ assert (cpu);
if (_txSetJmp())
{
$ _txSymGetFromAddr ((void*) 1);
}
$ _txClearJmp();
$ HANDLE process = GetCurrentProcess();
$ int frames = 0;
$ for (frames = -framesToSkip; frames < (int) szCapture; frames++)
{
$ DWORD64 prev = frame.AddrStack.Offset;
// Я злой и страшный серый walk. Я в поросятах знаю talk.
if (!_txSetJmp()) {$ break; }
#if defined (_GCC_VER)
if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$ break; }
#elif defined (_MSC_VER)
$ if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$ break; }
#else
#error _GCC_VER / _MSC_VER not defined
#endif
if (frames < 0) {$ continue; }
$ void* addr = (void*) frame.AddrPC.Offset;
if (frame.AddrFrame.Offset == 0) {$ addr = 0; } // Bad frame
if (frame.AddrStack.Offset < prev) {$ addr = (void*) -1; } // Strange frame
$ assert (0 <= frames && frames < (int) szCapture);
$ capture[frames] = addr;
}
$ _txClearJmp();
$ return frames;
}
// Note that Rick and Carl are speaking near the C language block. "C block", Carl. See: http://knowyourmeme.com/memes/carl
//-----------------------------------------------------------------------------------------------------------------
bool _txSymGetFromAddr (void* addr, Win32::SYMBOL_INFO** symbol /*= NULL*/,
Win32::IMAGEHLP_LINE64** line /*= NULL*/, const char** module /*= NULL*/,
const char** source /*= NULL*/, int context /*= 2*/)
{
$7 static HANDLE process = NULL;
#if defined (_GCC_VER)
#define LIB_ Win32::MinGW
#elif defined (_MSC_VER)
#define LIB_ Win32
#else
#error _GCC_VER / _MSC_VER not defined
#endif
$ if (!process && addr)
{
$ process = GetCurrentProcess();
$ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
$ _TX_CALL (LIB_::SymSetOptions, (options));
$ _TX_CALL (LIB_::SymInitialize, (process, NULL, true));
}
$ static DWORD64 mod = 0;
$ if (module)
{
$ static char sMod [MAX_PATH] = "";
$ memset (sMod, 0, sizeof (sMod));
$ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
$ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
$ char* ext = strrchr (sMod, '.');
if (ext) {$ _strlwr_s (ext, sizeof (sMod) - (ext-sMod)); }
$ *module = sMod;
}
$ static char buffer [_TX_BUFSIZE] = "";
$ static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
$ if (symbol)
{
$ memset (buffer, 0, sizeof (buffer));
$ sym->MaxNameLen = sizeof (buffer) - sizeof (Win32::SYMBOL_INFO) - 1;
$ sym->SizeOfStruct = sizeof (Win32::SYMBOL_INFO);
$ unsigned long long ofs = 0;
$ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
if (strcmp (sym->Name, "??") == 0) {$ *sym->Name = 0; }
$ *symbol = sym;
}
$ static Win32::IMAGEHLP_LINE64 line64 = { sizeof (line) };
$ if (line)
{
$ memset (&line64, 0, sizeof (line64));
$ DWORD ofs = 0;
$ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
$ *line = &line64;
}
$ if (source)
{
$ static char buf [_TX_BUFSIZE] = "";
$ memset (buf, 0, sizeof (buf));
$ if (line64.FileName && line64.LineNumber)
{
$ _txReadSource (buf, sizeof (buf) - 1, line64.FileName,
(int) line64.LineNumber - context, (int) line64.LineNumber + context, (int) line64.LineNumber);
$ *source = buf;
}
if (!*source || !**source) {$ *source = NULL; }
}
$ if (!addr && process)
{
$ _TX_CALL (LIB_::SymCleanup, (process));
$ process = NULL;
}
$ if (symbol)
{
$ if (strstr (sym->Name, "::TX::") ||
(strncmp (sym->Name, "_tx", 3) == 0 && isupper ((unsigned char) sym->Name[3])) ||
(strncmp (sym->Name, "tx", 2) == 0 && isupper ((unsigned char) sym->Name[2])) ||
strncmp (sym->Name, "_tx_", 4) == 0 ||
strncmp (sym->Name, "tx_", 3) == 0)
{
$ return true;
}
$ if (!line || !line64.FileName) return false;
$ intptr_t len = strlen (line64.FileName) - (sizeof (__FILE__) - 1);
$ return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
(len == 0 || line64.FileName[len-1] == '/' || line64.FileName[len-1] == '\\');
}
#undef LIB_
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _txReadSource (char buf[], intptr_t si";
break;
10793 default: $ sOp =
"совершить операцию 0x%lX";
break;
10797 $ PRINT_ (sOp, op);
10799 $
if (params >= 2) {$ PRINT_ ((
object?
" по адресу 0x%p" :
" по адресу NULL"),
object); }
10800 else {$ PRINT_ (
" (адрес не указан)"); }
10802 $
if (code == EXCEPTION_IN_PAGE_ERROR)
10804 $ PRINT_ (
", ошибка ввода-вывода:");
10808 $
unsigned long ntstatus = (
unsigned long) info[2];
10810 $ PRINT_ (
" 0x%lX (", ntstatus);
10812 $ s += FormatMessage (FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
10813 GetModuleHandle (
"NTDLL.DLL"), ntstatus, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
10814 s, (DWORD) (size - (s-what)), NULL) - 2;
10818 {$ PRINT_ (
" (не указана)"); }
10822 $ HMODULE module = NULL;
10823 $ _TX_CALL (Win32::GetModuleHandleEx, (GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (
const char*) addr, &module));
10827 $
static char sModule [MAX_PATH] =
"";
10828 $
int ok = GetModuleFileName (module, sModule,
sizeof (sModule));
10830 $
char* ext = (ok? strrchr (sModule,
'.') : NULL);
10831 $
if (ext)
_strlwr_s (ext,
sizeof (sModule) - 1 - (ext - sModule));
10833 if (ok) {$ PRINT_ (
" в модуле %s", sModule); }
10834 else {$ PRINT_ (
" в модуле 0x%p", module); }
10840 {$ PRINT_ (
" Дополнительно, превышен лимит исключений _TX_EXCEPTIONS_LIMIT (%d).",
_TX_EXCEPTIONS_LIMIT+0); }
10845 $ PRINT_ (
" Спасибо %s(), что сообщил. Люблю его <3", func);
10847 $
if (exc->ExceptionFlags & EXCEPTION_NONCONTINUABLE)
10848 {$ PRINT_ (
"\n\n" "Ой, всё (EXCEPTION_NONCONTINUABLE)."); }
10850 $
if (exc->ExceptionRecord)
10852 $ PRINT_ (
"\n\n" "Причина:" "\n\n");
10853 $ s += _txDumpExceptionSEH (s, size - (s-what), exc->ExceptionRecord, func);
10856 $
if (code == EXCEPTION_CPP_GCC ||
10857 code == EXCEPTION_CPP_GCC_UNWIND ||
10858 code == EXCEPTION_CPP_GCC_FORCED ||
10859 code == EXCEPTION_CPP_MSC)
10861 $ s += _txDumpExceptionCPP (s, size - (s-what), code, params, info);
10866 $
while (s > what && s[-1] ==
'\n') s--;
10867 $
if (s > what) s += _tx_snprintf_s (s, size - (s-what),
"\n\n");
10874 intptr_t _txDumpExceptionCPP (
char what[], intptr_t size,
10875 unsigned code ,
unsigned params ,
const ULONG_PTR info[] )
10884 #if defined (_GCC_VER)
10886 case EXCEPTION_CPP_GCC:
10887 case EXCEPTION_CPP_GCC_UNWIND:
10888 case EXCEPTION_CPP_GCC_FORCED:
10896 $
const std::type_info* type = NULL;
10897 $
void*
object = NULL;
10901 $ _Unwind_Exception* unwind_exception = (_Unwind_Exception*) info[0];
10902 $ ABI::__cxa_exception* cxa_exception = (ABI::__cxa_exception*) (unwind_exception + 1) - 1;
10904 $ type = cxa_exception->exceptionType;
10905 $
object = cxa_exception + 1;
10908 $ s += _txDumpExceptionObj (s, size - (s-what),
object, 0, type);
10917 using namespace abi;
10919 $ ABI::__cxa_exception* cxa_exception = __cxa_get_globals() -> caughtExceptions;
10921 $
if (cxa_exception && (cxa_exception->unwindHeader.exception_class & 1))
10923 $ cxa_exception = (((ABI::__cxa_exception*) (&cxa_exception->unwindHeader + 1) - 1) -> primaryException) - 1;
10926 $
if (cxa_exception)
10928 $
verify (cxa_exception->exceptionType == abi::__cxa_current_exception_type());
10930 $ s += _txDumpExceptionObj (s, size, cxa_exception + 1, 0, cxa_exception->exceptionType);
10935 #elif defined (_MSC_VER)
10937 case EXCEPTION_CPP_MSC:
10944 $
const std::type_info* type = NULL;
10945 $
void*
object = (params >= 2)? (
void*) info[1] : NULL;
10946 $
size_t szObj = 0;
10948 $
if (params >= 3 &&
10949 (info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER1 ||
10950 info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER2 ||
10951 info[0] == EXCEPTION_CPP_MSC_EH_MAGIC_NUMBER3 ||
10952 info[0] == EXCEPTION_CPP_MSC_EH_PURE_MAGIC_NUMBER1))
10954 $
auto throwInfo = (
const Win32::ThrowInfo*) info[2];
10956 $
if (throwInfo && throwInfo->pCatchableTypeArray)
10958 $ HMODULE module = (params >= 4)? (HMODULE) info[3] : NULL;
10960 #define RVA_(type, addr) ( (type) ((uintptr_t) module + (uintptr_t) (addr)) )
10962 $
const Win32::CatchableTypeArray* cArray = RVA_(
const Win32::CatchableTypeArray*, throwInfo->pCatchableTypeArray);
10963 $
const Win32::CatchableType* cType = RVA_(
const Win32::CatchableType*, cArray->arrayOfCatchableTypes[0]);
10965 $ type = RVA_(
const std::type_info*, cType->pType);
10966 $ szObj = cType->sizeOrOffset;
10972 $ s += _txDumpExceptionObj (s, size - (s-what),
object, szObj, type);
10999 $
while (s > what && s[-1] ==
'\n') s--;
11000 $
if (s > what) s += _tx_snprintf_s (s, size - (s - what),
"\n\n");
11002 $
return (s - what);
11007 intptr_t _txDumpExceptionObj (
char what[], intptr_t size,
void*
object,
size_t sizeObj,
const std::type_info* type)
11012 $
static char* s = NULL; s = what;
11013 $
static size_t szObj = 0; szObj = sizeObj;
11015 #define PRINT_(...) s += _tx_snprintf_s (s, size - (s - what), ##__VA_ARGS__)
11017 $ PRINT_ (
"\n\n" "Объект исключения C++:");
11019 $
const char* mangledName = (type)? type->name() : NULL;
11021 $
char* typeName = NULL;
11024 #if defined (_GCC_VER)
11025 $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err);
11028 $
const char* name = (!err && typeName)? typeName : mangledName;
11031 (strcmp (name,
"class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >") == 0 ||
11032 strcmp (name,
"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >") == 0))
11033 {$ name =
"std::string"; }
11036 (strcmp (name,
"class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > *") == 0 ||
11037 strcmp (name,
"class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > * __ptr64") == 0 ||
11038 strcmp (name,
"std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*") == 0))
11039 {$ name =
"std::string*"; }
11041 if (name) {$ PRINT_ (
" %s", name); }
11043 #if defined (_GCC_VER)
11052 #define PRINT_VAL_(fmt, typ, ...) \
11053 else if (*type == typeid ( typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
11054 else if (*type == typeid (const typ )) {$ PRINT_ (" = " #fmt, (* (typ* ) object) __VA_ARGS__); } \
11055 else if (*type == typeid ( typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
11056 else if (*type == typeid (const typ* )) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
11057 else if (*type == typeid ( typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); } \
11058 else if (*type == typeid (const typ* const)) {$ PRINT_ (" = " #fmt, (**(typ**) object) __VA_ARGS__); }
11061 PRINT_VAL_ (
"%s",
char*) PRINT_VAL_ (
'%c',
unsigned char) PRINT_VAL_ (%s,
bool, ?
"true" :
"false")
11062 PRINT_VAL_ ( %d,
int) PRINT_VAL_ ( %u,
unsigned int) PRINT_VAL_ (%g,
float)
11063 PRINT_VAL_ ( %hd,
short) PRINT_VAL_ ( %hu,
unsigned short) PRINT_VAL_ (%lg,
double)
11064 PRINT_VAL_ ( %ld,
long) PRINT_VAL_ ( %lu,
unsigned long) PRINT_VAL_ ('%c',
char)
11065 PRINT_VAL_ ("%s", std::
string, .c_str())
11067 else if (std::exception* e = dynamic_cast <std::exception*> ( (std::exception* )
object))
11069 $ PRINT_ (
", what(): \"%s\"", e->what());
11080 $
if (err &&
object && szObj)
11082 $
const unsigned char* buf = (
const unsigned char*)
object;
11084 $
if (szObj >= 64) szObj = 64;
11086 $ PRINT_ (
", дамп: [");
11087 $
for (
size_t i = 0; i < szObj; i++) PRINT_ (
"%c", (isprint (buf[i]) && !iscntrl (buf[i]))? buf[i] :
'.' );
11090 $
for (
size_t i = 0; i < szObj; i++) PRINT_ (
" %02X", buf[i]);
11096 {$ PRINT_ (
" = ???"); }
11098 $ PRINT_ ((
object?
"%sего адрес 0x%p." :
"%sего адрес NULL."), ((typeName || mangledName)?
", " :
""),
object);
11113 const char* _txCaptureStackBackTrace (
int framesToSkip ,
bool readSource ,
11114 CONTEXT* context , HANDLE thread )
11116 $6
const int maxFrames = 62;
11117 $
static char trace [(MAX_PATH + 1024+1) * maxFrames] =
"";
11119 if (framesToSkip == -1) {$
return trace; }
11121 $
static void* capture [maxFrames] = {};
11122 $
int frames = _txStackWalk (framesToSkip + !context,
sizearr (capture), capture, NULL, context, thread);
11124 $ memset (trace, 0,
sizeof (trace));
11127 #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (trace) - 1 - 3 - (s-trace), ##__VA_ARGS__)
11129 $
for (
int i = 0, n = 0; i < frames; i++)
11131 $
void* addr = capture[i];
11133 $ Win32::SYMBOL_INFO* sym = NULL;
11134 $ Win32::IMAGEHLP_LINE64* line = NULL;
11135 $
const char* module = NULL;
11136 $
const char* source = NULL;
11137 $
bool inTX =
false;
11139 if (addr) {$ inTX = _txSymGetFromAddr ((
char*) addr - 1, &sym, &line, &module); }
11140 if (readSource && !inTX) {$ _txSymGetFromAddr ((
void*) 1, NULL, NULL, NULL, &source, 2); }
11143 $
while (s > trace && s[-1] ==
'\n') { s--; nl++; }
11145 #if !defined (_TX_FULL_STACKTRACE)
11147 $
if (! ((sym && *sym->Name) || (line && ((line->FileName && *line->FileName) || line->LineNumber))))
11152 $ PRINT_ (
"%s#%2d 0x%p", ((n)? ((source || nl)?
"\n\n" :
"\n") :
""), i, addr);
11155 if (addr == 0) {$ PRINT_ (
" [Неверный фрейм]");
break; }
11156 if (addr == (
void*) -1) {$ PRINT_ (
" [Странный фрейм]");
break; }
11157 if (addr == (
void*)(uintptr_t) 0xBAADF00D) {$ PRINT_ (
" [Мусор от LocalAlloc()]");
break; }
11159 if (module) {$ PRINT_ (
" in %s%s", module, ((sym && *sym->Name)?
": " :
"")); }
11160 if (sym && *sym->Name) {$ PRINT_ (
"%s()", sym->Name); }
11161 if (line && line->FileName) {$ PRINT_ (
" at %s", line->FileName); }
11162 if (line && line->LineNumber) {$ PRINT_ (
" (%d)", (
int) line->LineNumber); }
11163 if (source) {$ PRINT_ (
":\n\n" "%s\n", source); }
11165 if (sym && strcmp (sym->Name ,
"main") == 0) {$
break; }
11168 #if defined (_MSC_VER)
11169 #pragma warning (disable: 28199) // Using possibly uninitialized memory '*s'
11172 $
while (s > trace && s[-1] ==
'\n') s--;
11175 #if defined (_MSC_VER)
11176 #pragma warning (default: 28199) // Using possibly uninitialized memory '*s'
11181 $ s += _tx_snprintf_s (s,
sizeof (trace) - 1 - (s-trace),
"");
11190 int _txStackWalk (
int framesToSkip,
size_t szCapture,
void* capture[],
unsigned long* ,
11191 CONTEXT* context , HANDLE thread )
11193 $6
namespace MinGW = Win32::MinGW;
11199 $ Win32::STACKFRAME64 frame = {};
11200 $ frame.AddrPC.Mode = frame.AddrStack.Mode = frame.AddrFrame.Mode = Win32::AddrModeFlat;
11202 $ CONTEXT ctx = {};
11203 $ ctx.ContextFlags |= CONTEXT_FULL;
11206 $
if (Win32::IsWow64Process) Win32::IsWow64Process (GetCurrentProcess(), &isWow64);
11207 else {$
return -1; }
11215 $
assert (Win32::RtlCaptureContext);
11216 $ _TX_CALLv (Win32::RtlCaptureContext, (&ctx));
11219 #if defined (_WIN64)
11223 $ Win32::WOW64_CONTEXT wow64ctx = {};
11224 $ wow64ctx.ContextFlags |= WOW64_CONTEXT_FULL;
11226 $
if (!_TX_CALL (Win32::Wow64GetThreadContext, (thread, &wow64ctx)))
11229 $ cpu = IMAGE_FILE_MACHINE_I386;
11231 $ frame.AddrPC .Offset = wow64ctx.Eip;
11232 $ frame.AddrStack.Offset = wow64ctx.Esp;
11233 $ frame.AddrFrame.Offset = wow64ctx.Ebp;
11237 $ cpu = IMAGE_FILE_MACHINE_AMD64;
11239 $ frame.AddrPC .Offset = ctx.Rip;
11240 $ frame.AddrStack.Offset = ctx.Rbp;
11241 $ frame.AddrFrame.Offset = ctx.Rsp;
11247 $ cpu = IMAGE_FILE_MACHINE_I386;
11249 $ frame.AddrPC .Offset = ctx.Eip;
11250 $ frame.AddrStack.Offset = ctx.Ebp;
11251 $ frame.AddrFrame.Offset = ctx.Esp;
11260 $ _txSymGetFromAddr ((
void*) 1);
11264 $ HANDLE process = GetCurrentProcess();
11267 $
for (frames = -framesToSkip; frames < (int) szCapture; frames++)
11269 $ DWORD64 prev = frame.AddrStack.Offset;
11273 if (!_txSetJmp()) {$
break; }
11275 #if defined (_GCC_VER)
11277 if (!_TX_CALL (MinGW::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
11278 MinGW::SymFunctionTableAccess64, MinGW::SymGetModuleBase64, NULL))) {$
break; }
11279 #elif defined (_MSC_VER)
11281 $
if (!_TX_CALL (Win32::StackWalk64, (cpu, process, thread, &frame, &ctx, NULL,
11282 Win32::SymFunctionTableAccess64, Win32::SymGetModuleBase64, NULL))) {$
break; }
11284 #error _GCC_VER / _MSC_VER not defined
11286 if (frames < 0) {$
continue; }
11288 $
void* addr = (
void*) frame.AddrPC.Offset;
11290 if (frame.AddrFrame.Offset == 0) {$ addr = 0; }
11291 if (frame.AddrStack.Offset < prev) {$ addr = (
void*) -1; }
11293 $
assert (0 <= frames && frames < (
int) szCapture);
11295 $ capture[frames] = addr;
11307 bool _txSymGetFromAddr (
void* addr, Win32::SYMBOL_INFO** symbol ,
11308 Win32::IMAGEHLP_LINE64** line ,
const char** module ,
11309 const char** source ,
int context )
11311 $7
static HANDLE process = NULL;
11313 #if defined (_GCC_VER)
11314 #define LIB_ Win32::MinGW
11316 #elif defined (_MSC_VER)
11320 #error _GCC_VER / _MSC_VER not defined
11323 $
if (!process && addr)
11325 $ process = GetCurrentProcess();
11327 $ DWORD options = SYMOPT_UNDNAME | SYMOPT_LOAD_LINES | SYMOPT_LOAD_ANYTHING | SYMOPT_INCLUDE_32BIT_MODULES |
11328 SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED | SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_NO_PROMPTS;
11330 $ _TX_CALL (LIB_::SymSetOptions, (options));
11331 $ _TX_CALL (LIB_::SymInitialize, (process, NULL,
true));
11334 $
static DWORD64 mod = 0;
11338 $
static char sMod [MAX_PATH] =
"";
11339 $ memset (sMod, 0,
sizeof (sMod));
11341 $ mod = _TX_CALL (LIB_::SymGetModuleBase64, (process, (uintptr_t) addr));
11343 $ GetModuleFileName ((HMODULE)(intptr_t) mod, sMod, MAX_PATH);
11345 $
char* ext = strrchr (sMod,
'.');
11346 if (ext) {$
_strlwr_s (ext,
sizeof (sMod) - (ext-sMod)); }
11352 $
static Win32::SYMBOL_INFO* sym = (Win32::SYMBOL_INFO*) buffer;
11356 $ memset (buffer, 0,
sizeof (buffer));
11358 $ sym->MaxNameLen =
sizeof (buffer) -
sizeof (Win32::SYMBOL_INFO) - 1;
11359 $ sym->SizeOfStruct =
sizeof (Win32::SYMBOL_INFO);
11360 $
unsigned long long ofs = 0;
11362 $ _TX_CALL (LIB_::SymFromAddr, (process, (uintptr_t) addr, &ofs, sym));
11364 if (strcmp (sym->Name,
"??") == 0) {$ *sym->Name = 0; }
11369 $
static Win32::IMAGEHLP_LINE64 line64 = {
sizeof (line) };
11373 $ memset (&line64, 0,
sizeof (line64));
11376 $ _TX_CALL (LIB_::SymGetLineFromAddr64, (process, (uintptr_t) addr, &ofs, &line64));
11384 $ memset (buf, 0,
sizeof (buf));
11386 $
if (line64.FileName && line64.LineNumber)
11388 $ _txReadSource (buf,
sizeof (buf) - 1, line64.FileName,
11389 (
int) line64.LineNumber - context, (
int) line64.LineNumber + context, (
int) line64.LineNumber);
11394 if (!*source || !**source) {$ *source = NULL; }
11397 $
if (!addr && process)
11399 $ _TX_CALL (LIB_::SymCleanup, (process));
11406 $
if (strstr (sym->Name,
"::TX::") ||
11407 (strncmp (sym->Name,
"_tx", 3) == 0 && isupper ((
unsigned char) sym->Name[3])) ||
11408 (strncmp (sym->Name,
"tx", 2) == 0 && isupper ((
unsigned char) sym->Name[2])) ||
11409 strncmp (sym->Name,
"_tx_", 4) == 0 ||
11410 strncmp (sym->Name,
"tx_", 3) == 0)
11415 $
if (!line || !line64.FileName)
return false;
11417 $ intptr_t len = strlen (line64.FileName) - (
sizeof (__FILE__) - 1);
11419 $
return (len >= 0 && _stricmp (line64.FileName + len, __FILE__) == 0) &&
11420 (len == 0 || line64.FileName[len-1] ==
'/' || line64.FileName[len-1] ==
'\\');
11430 intptr_t _txReadSource (
char buf[], intptr_t size,
const char file[],
11431 int linStart ,
int linEnd ,
int linMark )
11435 if (!file || !*file) {$
return 0; }
11437 if (linStart < 1) {$ linStart = 1; }
11438 if (linEnd == -1) {$ linEnd = INT_MAX; }
11442 if (!f) {$
return 0; }
11447 if (n >= linStart) {$
break; }
11448 while (!feof (f) && fgetc (f) !=
'\n')
11455 #define SZ_ ( size - 3 - (s - buf) )
11457 $
while (!feof (f) && SZ_ > 0)
11459 if (n > linEnd || _txNOP (SZ_) < 0) {$
break; }
11461 if (linMark != INT_MIN)
11462 {$ s += _tx_snprintf_s (s, SZ_,
"%s%5d: ", ((n == linMark)?
"=>" :
" "), n); }
11465 $
while (!feof (f) && SZ_ > 0 && (c = fgetc (f)) !=
'\n') *s++ = (char) c;
11466 if (c == EOF) {$ s--; }
11468 if (SZ_ > 0) {$ *s++ =
'\n'; }
11472 if (n <= linEnd && SZ_ <= 0)
11473 {$ s += _tx_snprintf_s (s, size - (s - buf),
"..."); }
11479 if (s > buf && s[-1] ==
'\n') {$ s--; }
11482 $
return (s - buf);
11487 const char* _txCaptureStackBackTraceTX (
int framesToSkip ,
bool readSource )
11489 $6
const int maxFrames = 62;
11490 $
static char trace [(MAX_PATH + 1024+1) * maxFrames] =
"";
11492 if (framesToSkip == -1) {$
return trace; }
11494 $ memset (trace, 0,
sizeof (trace));
11497 #define SZ_ ( sizeof (trace) - 1 - 3 - (s-trace) )
11499 $
const _txLoc* loc = &_txLoc::Cur;
11501 for (
int i = 0; loc && i < framesToSkip + 1; i++, loc = loc->prev) { $; }
11503 $
for (
int i = -framesToSkip; loc && i < maxFrames; i++, loc = loc->prev)
11505 if (i < 0) {$
continue; }
11507 if (loc->func || loc->file || loc->line)
11509 $ s += _tx_snprintf_s (s, SZ_,
"%s#%2d in %s at %s (%d)", (i? readSource?
"\n\n" :
"\n" :
""),
11510 i, loc->func, loc->file, loc->line);
11514 $ s += _tx_snprintf_s (s, SZ_,
":\n\n");
11515 $ s += _txReadSource (s, SZ_, loc->file, loc->line - 2, loc->line + 2, loc->line);
11522 $ s += _tx_snprintf_s (s,
sizeof (trace) - 1 - (s-trace),
"");
11529 bool _txCreateMiniDump (EXCEPTION_POINTERS* exc)
11533 $ DWORD winErr = GetLastError();
11534 $
int crtErr = errno;
11535 #if !defined (__CYGWIN__)
11536 $
unsigned long dosErr = _doserrno;
11539 $
static char dumpName[MAX_PATH] =
"";
11540 if (!*dumpName) {$ _tx_snprintf_s (dumpName,
sizeof (dumpName) - 1,
"%s.dmp",
_txLogName); }
11542 $ HANDLE file = CreateFile (dumpName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
11544 if (!file || file == INVALID_HANDLE_VALUE) {$
return false; }
11546 $ Win32::MINIDUMP_EXCEPTION_INFORMATION excInfo = { GetCurrentThreadId(), exc,
false };
11548 $
bool ok = _TX_CALL (Win32::MiniDumpWriteDump, (GetCurrentProcess(), GetCurrentProcessId(), file,
11549 (Win32::MINIDUMP_TYPE) (Win32::MiniDumpWithIndirectlyReferencedMemory | Win32::MiniDumpScanMemory),
11550 &excInfo, NULL, NULL));
11551 $ CloseHandle (file);
11553 $ SetLastError (winErr);
11555 #if !defined (__CYGWIN__)
11556 $ _doserrno = dosErr;
11559 if (ok) {$
return true; }
11560 else {$
return false; }
11570 const char* _txProcessError (
const char file[],
int line,
const char func[],
unsigned color,
const char msg[], va_list args)
11574 DWORD winErr = GetLastError();
11576 int crtErr = errno;
11578 #if !defined (__CYGWIN__)
11579 unsigned long dosErr = _doserrno;
11581 unsigned long dosErr = 0;
11584 unsigned oglErr = _TX_CALL (Win32::wglGetCurrentDC, ())? _TX_CALL (Win32::glGetError, ()) : _txOGLError;
11586 unsigned threadId = GetCurrentThreadId();
11588 enum { isFatal = 0x01, isWarning = 0x02, noMsgBox = 0x04, fmtOnly = 0x08, traceSE = 0x10 };
11589 unsigned options = 0;
11591 for (; msg && *msg; msg++)
11593 if (*msg ==
'\a') options |= isFatal;
11594 else if (*msg ==
'\v') options |= isWarning;
11595 else if (*msg ==
'\b') options |= noMsgBox;
11596 else if (*msg ==
'\f') options |= fmtOnly;
11597 else if (*msg ==
'\t') options |= traceSE;
11601 const char* stkTrace = NULL;
11602 const char* txTrace = NULL; (void) txTrace;
11604 if (!(options & fmtOnly))
11606 stkTrace = ((options & traceSE) && *_txTraceSE)? _txTraceSE : _txCaptureStackBackTrace (2,
true);
11607 txTrace = _txCaptureStackBackTraceTX (0,
true);
11614 #define PRINT_(...) s += _tx_snprintf_s (s, sizeof (what) - 1 - (s - what), ##__VA_ARGS__)
11615 #define VPRINT_(...) s += _tx_vsnprintf_s (s, sizeof (what) - 1 - (s - what), ##__VA_ARGS__)
11617 PRINT_ (
"TXLib %s\n\n", ((options & isWarning)?
"предупреждает:" :
11618 (options & isFatal)?
"соболезнует..." :
11621 if (file) PRINT_ (
", файл: %s", file);
11622 if (line) PRINT_ (
", строка: %d", line);
11623 if (func) PRINT_ (
", функция: %s", func);
11626 if (msg) PRINT_ (
"%s: ", (file || line || func)?
"Сообщение : "ВНЕЗАПНО"),
VPRINT_ (msg, args);
while (s > what && s[-1] == '\n') s--;
PRINT_ ("\n\n" "#%d: %s, Instance: 0x%p (%d-bit), Flags: %c%c%c%c%c%d, Thread: 0x%X%s",
_txErrors, _TX_VERSION, &_txInitialized, (sizeof (void*) == 4)? 32 : 64,
"cC"[_txConsole], "mM"[_txMain], "dD"[_txIsDll], "rR"[_txRunning], "eE"[_txExit], _txLoc::Cur.trace,
threadId, (threadId == _txMainThreadId)? " (Main)" :
(threadId == _txCanvas_ThreadId)? " (Canvas)" : "");
if (winErr) PRINT_ (", GetLastError(): %lu (", (unsigned long) winErr),
s += FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, winErr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
s, (DWORD) (sizeof (what) - (s-what)), NULL) - 2,
s -= (s[-1] == '.')? 1 : 0,
PRINT_ (")");
if (crtErr) PRINT_ (", errno: %d (%s)", crtErr, (strerror_s (str, sizeof (str), crtErr), str));
if (dosErr) PRINT_ (", _doserrno: %lu (%s)", dosErr, (strerror_s (str, sizeof (str), dosErr), str));
if (oglErr) PRINT_ (", glGetError(): %u (0x%04X, %s)", oglErr, oglErr, _TX_CALL (Win32::gluErrorString, (oglErr)));
#if (__cplusplus <= 201703L)
PRINT_ (". %s\n", ::std::uncaught_exception ()? "std::uncaught_exception(): true." : "");
#else
PRINT_ (". %s\n", ::std::uncaught_exceptions()? "std::uncaught_exceptions(): true." : "");
#endif
if (_txLoc::Cur.inTX > 0 && file && !(_txLoc::Cur.line == line && _stricmp (_txLoc::Cur.file, file) == 0) &&
(_txLoc::Cur.file || _txLoc::Cur.line || _txLoc::Cur.func))
PRINT_ ("From: %s (%d) %s.\n", _txLoc::Cur.file, _txLoc::Cur.line, _txLoc::Cur.func);
txOutputDebugPrintf ("\r" "%s - ERROR: %s\n", _TX_VERSION, what);
if (options & fmtOnly)
{
SetLastError (winErr);
errno = crtErr;
#if !defined (__CYGWIN__)
_doserrno = dosErr;
#endif
return what;
}
unsigned restore = txGetConsoleAttr();
txSetConsoleAttr ((options & isFatal)? FOREGROUND_LIGHTMAGENTA : FOREGROUND_LIGHTRED);
if (color) {$ txSetConsoleAttr (color); }
int oldCodePage = txSetLocale();
fprintf (stderr, "\n" "--------------------------------------------------\n"
"%s\n"
"--------------------------------------------------\n",
what);
if (stkTrace && strstr (stkTrace, ".exe: "))
{$ fprintf (stderr, "Стек вызовов:\n\n"
"%s\n\n"
"--------------------------------------------------\n",
stkTrace); }
SetConsoleOutputCP (oldCodePage);
txSetConsoleAttr (restore);
if (*_txLogName) do
{
FILE* log = NULL; fopen_s (&log, _txLogName, "a");
if (!log) {$ break; }
fprintf (log, "\n" "--------------------------------------------------\n"
"%s\n"
"--------------------------------------------------\n",
what);
fprintf (log, "Стек вызовов:\n\n"
"%s\n",
(*_txTraceSE? _txTraceSE : stkTrace));
#if defined (_TX_ALLOW_TRACE) || defined (_DEBUG)
if (_txLoc::Cur.inTX > 0 && txTrace && *txTrace)
{
fprintf (log, "\n" "--------------------------------------------------\n"
"Стек вызовов TX:\n\n"
"%s\n",
txTrace);
}
#endif
fprintf (log, "\n" "--------------------------------------------------\n"
"%s\n\n"
"--------------------------------------------------\n",
_txAppInfo());
fclose (log);
break;
}
while (false);
int ret = 0;
if (!(options & noMsgBox))
{
txSleep (_txWindowUpdateInterval);
PRINT_ ("\n" "Прервать программу?");
ret = txMessageBox (what, ((options & isWarning)? "Предупреждение" :
(options & isFatal)? "Фатальная ошибка" :
"Ошибка в программе"), MB_ICONSTOP | MB_SYSTEMMODAL | MB_YESNOCANCEL);
}
SetLastError (winErr);
errno = crtErr;
#if !defined (__CYGWIN__)
_doserrno = dosErr;
#endif
if (((options & isFatal) && !IsDebuggerPresent()) || ret == IDYES)
{
txUnlock();
_txCleanup();
Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
}
#undef PRINT_
return what;
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
int _txOnErrorReport (int type, const char* text, int* ret)
{
assert (text);
assert (ret);
_txErrors++;
unsigned restore = txGetConsoleAttr();
switch (type)
{
case _CRT_WARN: txSetConsoleAttr (FOREGROUND_LIGHTRED); break;
case _CRT_ERROR: txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA); break;
case _CRT_ASSERT: txSetConsoleAttr (FOREGROUND_YELLOW); break;
default: break;
}
const char startReport[] = "Detected memory leaks!\n",
endReport[] = "Object dump complete.\n";
if (strcmp (text, startReport) == 0) // Dirty, dirty hack. А что делать?
{
_txOnErrorReport (type, "\n", NULL);
_txOnErrorReport (type, _TX_VERSION " - ERROR: ", NULL);
_txOnErrorReport (type, "Внимание: Обнаружены утечки памяти! (Для поиска используйте _TX_ALLOC_BREAK.)\n", NULL);
_txOnErrorReport (type, "\n", NULL);
}
size_t len = strlen (text);
if (text [len-1] != '\n') txOutputDebugPrintf ("%s", text);
else if (strcmp (text, endReport) != 0) txOutputDebugPrintf ("%s" "%s - ERROR: ", text, _TX_VERSION);
else txOutputDebugPrintf ("%s\n", text);
DWORD n = 0;
HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
WriteFile (err, text, (DWORD) strlen (text), &n, NULL);
txSetConsoleAttr (restore);
if (*_txLogName) do
{
HANDLE log = CreateFile (_txLogName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (log == INVALID_HANDLE_VALUE) break;
SetFilePointer (log, 0, NULL, FILE_END);
WriteFile (log, text, (DWORD) strlen (text), &n, NULL);
CloseHandle (log);
break;
}
while (false);
if (ret) *ret = 0;
return (type == _CRT_WARN);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
int txMessageBox (const char text[], const char header[], unsigned flags /*= MB_ICONINFORMATION | MB_OKCANCEL*/)
{
$5 static wchar_t textW [_TX_BIGBUFSIZE * sizeof (wchar_t)] = L"[NULL text]";
$ static wchar_t headerW [_TX_BUFSIZE * sizeof (wchar_t)] = L"[NULL header]";
if (text) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, text, -1, textW, sizearr (textW)) || memset (textW, 0, sizeof (textW)); }
if (header) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, header, -1, headerW, sizearr (headerW)) || memset (headerW, 0, sizeof (headerW)); }
$ HWND wnd = _txCanvas_Window;
$ int ret = MessageBoxW ((wnd && IsWindowVisible (wnd))? wnd : _TX_CALL (Win32::GetConsoleWindow,()),
textW, headerW, flags | MB_SETFOREGROUND | MB_TOPMOST | MB_OKCANCEL );
$ if (ret == IDCANCEL)
{
$ SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0);
$ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT);
}
$ return ret;
}
//-----------------------------------------------------------------------------------------------------------------
bool txGetAsyncKeyState (int key)
{
$1 HWND wnd = GetForegroundWindow();
return (GetAsyncKeyState (key) & 0x8000) &&
(wnd == txWindow() || wnd == Win32::GetConsoleWindow());
}
//-----------------------------------------------------------------------------------------------------------------
bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...)
{
$5 if (_TX_ARGUMENT_FAILED (format)) return false;
$ va_list arg; va_start (arg, format);
$ bool ok = true;
#if defined (_WIN32_IE) && (_WIN32_IE >= 0x0500)
$ NOTIFYICONDATA nid = { sizeof (nid) };
$ nid.uFlags = NIF_ICON | NIF_TIP | NIF_INFO;
$ nid.hWnd = NULL;
$ nid.uID = 1;
$ nid.hIcon = _txCreateTXIcon (16); assert (nid.hIcon);
$ strncpy_s (nid.szTip, sizeof (nid.szTip), "TXLib Information", sizeof (nid.szTip));
$ strncpy_s (nid.szInfoTitle, sizeof (nid.szInfoTitle), (title? title : "TXLib сообщает"), sizeof (nid.szInfoTitle) - 1);
$ _tx_vsnprintf_s (nid.szInfo, sizeof (nid.szInfo), format, arg);
$ nid.dwInfoFlags = flags;
$ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification)\n", nid.szInfoTitle, nid.szInfo);
$ ok &= !!Shell_NotifyIcon (NIM_ADD, (::NOTIFYICONDATA*) &nid);
$ ok &= !!Shell_NotifyIcon (NIM_MODIFY, (::NOTIFYICONDATA*) &nid);
$ if (nid.hIcon) DestroyIcon (nid.hIcon) asserted;
#else
$ char nid_szInfo[_TX_BUFSIZE] = "";
$ _tx_vsnprintf_s (nid_szInfo, sizeof (nid_szInfo), format, arg);
$ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification - NOT displayed)\n", title, nid_szInfo);
$ ok = false;
$ (void)flags; (void)title;
#endif
$ va_end (arg);
return ok;
}
//-----------------------------------------------------------------------------------------------------------------
void _txTrace (const char file[], int line, const char func[], const char msg[] /*= NULL*/, ...)
{
unsigned id = GetCurrentThreadId();
const char marks[2][2][3] = {{"uU", "cC"}, {"mM", "??"}};
char mark = marks [id == _txMainThreadId] [id == _txCanvas_ThreadId] [(_txLoc::Cur.inTX > 0)];
char msgStr[_TX_BUFSIZE] = "";
if (msg)
{
va_list arg; va_start (arg, msg);
_tx_vsnprintf_s (msgStr, sizeof (msgStr) - 1, msg, arg);
va_end (arg);
}
txOutputDebugPrintf ("%s - 0x%p %c%c%c%c%c%d [%c] - %-*s (%5d) " "|%*s%s" "%s%s\n",
_TX_VERSION, &_txInitialized,
"cC"[_txConsole], "mM"[_txMain], "dD"[_txIsDll], "rR"[_txRunning], "eE"[_txExit],
_txLoc::Cur.trace, mark,
(int) sizeof (__FILE__) - 1, (file? file : "(NULL file)"), line,
2 * (_txLoc::Cur.inTX - 1) * !!func, "", (func? func : ""),
((*msgStr && func)? ": " : ""), msgStr);
}
//-----------------------------------------------------------------------------------------------------------------
int txOutputDebugPrintf (const char format[], ...)
{
if (!format) return 0;
enum { msgbox = 1, print = 2, compr = 4 };
int options = 0;
for (; format && *format; format++)
{
if (*format == '\a') options |= msgbox;
else if (*format == '\f') options |= print;
else if (*format == '\r') options |= compr;
else break;
}
char text[_TX_BIGBUFSIZE] = "";
va_list arg; va_start (arg, format);
int n = (int) _tx_vsnprintf_s (text, sizeof (text) - 1-1, format, arg);
va_end (arg);
struct __ { static int trimSpaces (char str[])
{
char *dst = str, *src = str;
for (char d = ' '; d; src++)
if (isspace ((unsigned char)(*src))) { if (d != ' ') *dst++ = d = ' '; }
else *dst++ = d = *src;
return (int) (dst - str - 1);
}};
if (options & compr) n = __::trimSpaces (text);
OutputDebugString (text);
if (options & print) fprintf (stderr, "%s", text);
if (options & msgbox) txMessageBox (text, "Оказывается, что", MB_ICONEXCLAMATION);
return n;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...)
{
if (!format) return 0;
va_list arg; va_start (arg, format);
intptr_t ret = _tx_vsnprintf_s (stream, size, format, arg);
va_end (arg);
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg)
{
if (!stream || !format) return 0;
#if defined (_TRUNCATE)
intptr_t ret = _vsnprintf_s (stream, size, _TRUNCATE, format, arg);
#else
intptr_t ret = _vsnprintf (stream, size, format, arg);
#endif
if (ret < 0 && size >= 4)
{
const char ellipsis[] = "...";
size_t szEllipsis = sizeof (ellipsis) - 1;
strncpy_s (stream + size - szEllipsis, szEllipsis+1, ellipsis, szEllipsis);
}
return (ret >= 0)? ret : size;
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (__CYGWIN__)
int _getch()
{
termios oldattr = {}; tcgetattr (STDIN_FILENO, &oldattr);
termios newattr = oldattr;
newattr.c_lflag &= ~(ICANON | ECHO);
tcsetattr (STDIN_FILENO, TCSANOW, &newattr);
int ch = getchar();
tcsetattr (STDIN_FILENO, TCSANOW, &oldattr);
return ch;
}
//-----------------------------------------------------------------------------------------------------------------
int _putch (int ch)
{
termios old = {}; tcgetattr (STDOUT_FILENO, &old);
termios cur = old;
cur.c_lflag &= ~ICANON;
cur.c_lflag |= ECHO;
tcsetattr (STDOUT_FILENO, TCSANOW, &cur);
putchar (ch);
tcsetattr (STDOUT_FILENO, TCSANOW, &old);
return ch;
}
//-----------------------------------------------------------------------------------------------------------------
int _kbhit()
{
termios old = {}; tcgetattr (STDIN_FILENO, &old);
termios cur = old;
cur.c_lflag &= ~(ICANON | ECHO);
cur.c_cc[VMIN] = 1;
cur.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &cur);
fd_set fd = {}; FD_SET (STDIN_FILENO, &fd);
timeval tv = {};
int res = select (STDIN_FILENO + 1, &fd, NULL, NULL, &tv) && FD_ISSET (STDIN_FILENO, &fd);
tcsetattr (STDIN_FILENO, TCSAFLUSH, &old);
return res;
}
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Information
//-----------------------------------------------------------------------------------------------------------------
const char* txGetModuleFileName (bool fileNameOnly /*= true*/)
{
static char name[MAX_PATH] = "";
if (!*name)
{
if (!GetModuleFileName (NULL, name, sizeof (name) - 1)) *name = 0;
char* ext = strrchr (name, '.');
if (ext) _strlwr_s (ext, sizeof (name) - 1 - (ext - name));
}
assert (*name);
if (fileNameOnly) return name;
static char fullName[MAX_PATH] = "";
strncpy_s (fullName, sizeof (fullName), name, sizeof (fullName) - 1);
char* title = strrchr (fullName, '\\'); if (!title) title = fullName;
char* ext = strrchr (fullName, '.'); if (!ext) ext = fullName + strlen (fullName);
size_t sz = sizeof (fullName) - (ext - fullName);
strncpy_s (ext, sz-1, " - TXLib", sz);
return title + 1;
}
//-----------------------------------------------------------------------------------------------------------------
const char* _txAppInfo()
{
$1 time_t timeT = time (NULL) - clock()/CLOCKS_PER_SEC;
char timeS[32] = "";
ctime_s (timeS, sizeof (timeS), &timeT);
static char text[_TX_BUFSIZE] = "";
char cwd [MAX_PATH] = "";
_tx_snprintf_s (text, sizeof (text) - 1,
"Developed with:\n\n"
"The Dumb Artist Library (TX Library)\n"
_TX_VERSION "\n" _TX_AUTHOR "\n"
"See license on: http://txlib.ru\n\n"
"TXLib file:" "\t" __FILE__ "\n"
"Compiled:" "\t" __DATE__ " " __TIME__ ", " __TX_COMPILER__ ", %s, %d-bit, " _TX_BUILDMODE "\n"
"Started:" "\t" "%.6s %.4s %.8s\n\n"
"Run file:" "\t" "%s\n"
"Directory:" "\t" "%s",
#if defined (_MSC_VER)
"MSVC Runtime",
#elif defined (__CYGWIN__)
"Cygwin Runtime",
#elif defined (_GCC_VER) && defined (_WIN64)
__mingw_get_crt_info(),
#else
"MinGW Runtime " TX_QUOTE (__MINGW32_MAJOR_VERSION) "." TX_QUOTE (__MINGW32_MINOR_VERSION),
#endif
(sizeof (void*) == sizeof (DWORD))? 32 : 64,
timeS + 4, timeS + 20, timeS + 11, // These offsets are ANSI standardized
txGetModuleFileName(),
_getcwd (cwd, sizeof (cwd) - 1));
return text;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//! @}
//}
//=================================================================================================================
//=================================================================================================================
//{ TXLib API implementation
// Реализация TXLib API
//=================================================================================================================
inline const char* txVersion()
{
return _TX_VERSION;
}
//-----------------------------------------------------------------------------------------------------------------
inline unsigned txVersionNumber()
{
return _TX_VER;
}
//-----------------------------------------------------------------------------------------------------------------
inline HWND txWindow()
{
$0 return _txCanvas_Window;
}
//-----------------------------------------------------------------------------------------------------------------
inline HDC& txDC()
{
$0 return _txCanvas_BackBuf[0];
}
//-----------------------------------------------------------------------------------------------------------------
inline RGBQUAD* txVideoMemory()
{
return _txCanvas_Pixels;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetExtent (HDC dc /*= txDC()*/)
{
$0 static POINT err = {-1, -1};
if (!dc) {$ POINT screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; return screen; };
if (_TX_DEFAULT_HDC_FAILED (dc)) {$ return err; }
$ BITMAP bmap = {};
$ txGDI (Win32::GetObject (Win32::GetCurrentObject (dc, OBJ_BITMAP), sizeof (bmap), &bmap), dc) asserted;
$ POINT size = { bmap.bmWidth, bmap.bmHeight };
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetExtentX (HDC dc /*= txDC()*/)
{
return txGetExtent (dc) .x;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetExtentY (HDC dc /*= txDC()*/)
{
return txGetExtent (dc) .y;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDestroyWindow (HWND wnd /*= txWindow()*/)
{
$1 if (!wnd || !txWindow()) return false;
$ if (wnd != txWindow())
{
$ return !!SendNotifyMessage (txWindow(), _TX_WM_DESTROYWND, 0, (LPARAM) wnd);
}
$ if (SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0) == 0) return false;
$ if (_txMain)
{
$ txNotifyIcon (NIIF_WARNING, NULL, "\n" "Очень, очень плохо завершать программу через txDestroyWindow().\n\n"
"Возвращайтесь через main(), там вам будут рады.\n");
$ Sleep (_TX_TIMEOUT);
}
$ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT);
$ return _txCanvas_Window == NULL;
}
//-----------------------------------------------------------------------------------------------------------------
HPEN txSetColor (COLORREF color, double thickness /*= 1*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HPEN pen = Win32::CreatePen ((color == TX_TRANSPARENT? PS_NULL : PS_SOLID), ROUND (thickness), color);
$ if (!pen) return (HPEN) NULL;
$ if (!_txBuffer_Select (pen, dc))
{
$ Win32::DeleteObject (pen);
$ return (HPEN) NULL;
}
$ if (txGDI (Win32::SetTextColor (dc, color), dc) == CLR_INVALID)
{$ return (HPEN) NULL; }
$ return pen;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txColor (double red, double green, double blue)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255));
$ return txSetColor (color)? color : CLR_INVALID;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txGetColor (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_PEN)), dc);
$ assert (obj); if (!obj) return CLR_INVALID;
$ union { EXTLOGPEN extLogPen; LOGPEN LogPen; } buf = {};
$ int size = Win32::GetObject (obj, 0, NULL);
$ Win32::GetObject (obj, sizeof (buf), &buf) asserted;
$ return (size == sizeof (LOGPEN))? buf.LogPen.lopnColor : buf.extLogPen.elpColor;
}
//-----------------------------------------------------------------------------------------------------------------
HBRUSH txSetFillColor (COLORREF color, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HBRUSH brush = (color == TX_TRANSPARENT)? (HBRUSH) Win32::GetStockObject (HOLLOW_BRUSH) : Win32::CreateSolidBrush (color);
$ return (_txBuffer_Select (brush, dc))? brush : (Win32::DeleteObject (brush), (HBRUSH) NULL);
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txFillColor (double red, double green, double blue)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255));
$ return txSetFillColor (color)? color : CLR_INVALID;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txGetFillColor (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_BRUSH)), dc);
$ assert (obj); if (!obj) return CLR_INVALID;
$ LOGBRUSH buf = {};
$ txGDI ((Win32::GetObject (obj, sizeof (buf), &buf)), dc) asserted;
$ return buf.lbColor;
}
//-----------------------------------------------------------------------------------------------------------------
bool txClear (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT size = txGetExtent (dc);
$ return txGDI (!!(Win32::PatBlt (dc, 0, 0, size.x, size.y, PATCOPY)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txSetPixel (double x, double y, COLORREF color, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ txGDI ((Win32::SetPixel (dc, ROUND (x), ROUND (y), color)), dc);
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txPixel (double x, double y, double red, double green, double blue, HDC dc /*= txDC()*/)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ return txSetPixel (x, y, RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
inline COLORREF txGetPixel (double x, double y, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ return txGDI ((Win32::GetPixel (dc, ROUND (x), ROUND (y))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txLine (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ bool ok = txGDI ((Win32::MoveToEx (dc, ROUND (x0), ROUND (y0), NULL)), dc);
$ ok &= txGDI ((Win32::LineTo (dc, ROUND (x1), ROUND (y1) )), dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txRectangle (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::Rectangle (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txPolygon (const POINT points[], int numPoints, HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (points)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI (!!(Win32::Polygon (dc, points, numPoints)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txEllipse (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::Ellipse (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txCircle (double x, double y, double r)
{
$1 return txEllipse (x-r, y-r, x+r, y+r);
}
//-----------------------------------------------------------------------------------------------------------------
bool txArc (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Arc (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txPie (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Pie (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txChord (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Chord (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txFloodFill (double x, double y,
COLORREF color /*= TX_TRANSPARENT*/, DWORD mode /*= FLOODFILLSURFACE*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ if (color == TX_TRANSPARENT) color = txGetPixel (x, y, dc);
$ return txGDI (!!(Win32::ExtFloodFill (dc, ROUND (x), ROUND (y), color, mode)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txTextOut (double x, double y, const char text[], HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (text)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ int len = (int) strlen (text);
$ bool ok = txGDI (!!(Win32::TextOut (dc, ROUND (x), ROUND (y), text, len)), dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDrawText (double x0, double y0, double x1, double y1, const char text[],
unsigned format /*= DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS*/,
HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (text)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
#if !defined (NDEBUG)
$ if (x0 > x1)
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Параметр x0 = %lg больше, чем x1 = %lg. Текст выводиться не будет.", x0, x1);
}
$ if (y0 > y1)
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Параметр y0 = %lg больше, чем y1 = %lg. Текст выводиться не будет.", y0, y1);
}
#endif
$ RECT r = { ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1) };
$ if (!strchr (text, '\n')) format |= DT_SINGLELINE;
$ unsigned prev = txSetTextAlign (TA_LEFT | TA_TOP | TA_NOUPDATECP, dc);
$ bool ok = false;
$ if (Win32::DrawText)
{
$ ok = !!txGDI ((Win32::DrawText (dc, text, -1, &r, format)), dc);
$ Win32::GetPixel (dc, 0, 0);
$ ok = true;
}
else
{
$ txTextOut ((x0 + x1) / 2, (y0 + y1) / 2, text);
$ ok = false;
}
$ txSetTextAlign (prev, dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
HFONT txSelectFont (const char name[], double sizeY, double sizeX /*= -1*/,
int bold /*= FW_DONTCARE*/, bool italic /*= false*/, bool underline /*= false*/,
bool strikeout /*= false*/, double angle /*= 0*/,
HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (name)) return NULL;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HFONT font = txFontExist (name)?
Win32::CreateFont (ROUND (sizeY), ROUND ((sizeX >= 0)? sizeX : sizeY/3),
ROUND (angle*10), 0, bold, italic, underline, strikeout,
RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, name)
:
(HFONT) Win32::GetStockObject (SYSTEM_FIXED_FONT);
$ _txBuffer_Select (font, dc);
$ return font;
}
//-----------------------------------------------------------------------------------------------------------------
SIZE txGetTextExtent (const char text[], HDC dc /*= txDC()*/)
{
$1 SIZE size = {-1, -1};
$ if (_TX_ARGUMENT_FAILED (text)) return size;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return size;
$ size_t len = strlen (text);
$ txGDI ((Win32::GetTextExtentPoint32 (dc, text, (int) len, &size)), dc) asserted;
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetTextExtentX (const char text[], HDC dc /*= txDC()*/)
{
$1 return txGetTextExtent (text, dc) .cx;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetTextExtentY (const char text[], HDC dc /*= txDC()*/)
{
$1 return txGetTextExtent (text, dc) .cy;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txSetTextAlign (unsigned align /*= TA_CENTER | TA_BASELINE*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::SetTextAlign (dc, align)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
LOGFONT* txFontExist (const char name[])
{
$1 if (_TX_ARGUMENT_FAILED (name)) return NULL;
$ static LOGFONT font = {};
$ font.lfCharSet = DEFAULT_CHARSET;
$ strncpy_s (font.lfFaceName, sizeof (font.lfFaceName), name, sizeof (font.lfFaceName) - 1);
$ struct tools
{
static int CALLBACK enumFonts (const LOGFONT* fnt, const TEXTMETRIC*, DWORD, LPARAM data)
{
$ if (_TX_ARGUMENT_FAILED (fnt)) return 0;
$ if (_TX_ARGUMENT_FAILED (data)) return 0;
#ifndef __STRICT_ANSI__
$ return _strnicmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
#else
$ return strncmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
#endif
}
};
$ return txGDI ((Win32::EnumFontFamiliesEx (NULL, &font, tools::enumFonts, (LPARAM) &font, 0)), NULL) == 0? &font : NULL;
}
//-----------------------------------------------------------------------------------------------------------------
bool txSelectObject (HGDIOBJ obj, HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (obj)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return _txBuffer_Select (obj, dc);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateCompatibleDC (double sizeX, double sizeY, HBITMAP bitmap /*= NULL*/)
{
$1 POINT size = { ROUND (sizeX), ROUND (sizeY) };
$ HDC dc = _txBuffer_Create (NULL, &size, bitmap);
$ assert (dc); if (!dc) return NULL;
$ txSetDefaults (dc);
$ if (!_txCanvas_UserDCs) return dc;
$ txAutoLock _lock;
$ _txCanvas_UserDCs->push_back (dc);
$ if (_txCanvas_UserDCs->size() >= _TX_BUFSIZE)
{$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загрузили уже %d HDC, системе может стать плохо.", (int) _txCanvas_UserDCs->size()); }
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateDIBSection (double sizeX, double sizeY, RGBQUAD** pixels /*= NULL*/)
{
$1 RGBQUAD* buf = NULL;
$ if (!pixels) pixels = &buf;
$ BITMAPINFO info = {{ sizeof (info), ROUND (sizeX), ROUND (sizeY), 1, WORD (sizeof (RGBQUAD) * 8), BI_RGB }};
$ HDC dc = txCreateCompatibleDC (0, 0, Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (void**) pixels, NULL, 0));
$ RGBQUAD black = { 0, 0, 0, 255 };
$ for (int i = 0; i < sizeX * sizeY; i++) (*pixels)[i] = black;
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateDIBSection (double sizeX, double sizeY, COLORREF** pixels)
{
$1 return txCreateDIBSection (sizeX, sizeY, (RGBQUAD**) pixels);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txLoadImage (const char filename[], unsigned imageFlags /*= IMAGE_BITMAP*/, unsigned loadFlags /*= LR_LOADFROMFILE*/)
{
$1 if (_TX_ARGUMENT_FAILED (filename && *filename)) return NULL;
$ HBITMAP image = (HBITMAP) Win32::LoadImage ((loadFlags & LR_LOADFROMFILE)? NULL : GetModuleHandle (NULL),
filename, imageFlags, 0, 0, loadFlags);
$ if (!image) return NULL;
$ HDC dc = txCreateCompatibleDC (0, 0, image);
$ if (!(loadFlags & LR_LOADFROMFILE)) return dc;
$ static std::map <std::string, unsigned> loadTimes;
$ std::string file = filename;
$ unsigned time = GetTickCount();
$ if ((long) (time - loadTimes [file]) < _TX_TIMEOUT)
{$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загружаете \"%s\" слишком часто, программа будет тормозить.", filename); }
$ loadTimes [file] = time;
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDeleteDC (HDC* pdc)
{
$1 if (_TX_ARGUMENT_FAILED (pdc)) return false;
$ HDC dc = *pdc;
$ bool ok = _txBuffer_Delete (pdc);
$ if (!ok) return false;
$ if (!_txCanvas_UserDCs) return ok;
$ txAutoLock _lock;
$ std::vector <HDC> ::iterator it = std::find (_txCanvas_UserDCs->begin(), _txCanvas_UserDCs->end(), dc);
$ if (it != _txCanvas_UserDCs->end()) { std::swap (*it, _txCanvas_UserDCs->back()); _txCanvas_UserDCs->pop_back(); }
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDeleteDC (HDC dc)
{
$1 return txDeleteDC (&dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txBitBlt (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, unsigned operation /*= SRCCOPY*/)
{
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
$ return txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), operation)), destImage);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txBitBlt (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txBitBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource);
}
//-----------------------------------------------------------------------------------------------------------------
bool txTransparentBlt (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, COLORREF transColor /*= TX_BLACK*/)
{
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
#if !defined (NDEBUG)
$ if (!(0 <= xSource && xSource + width <= size.x &&
0 <= ySource && ySource + height <= size.y))
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
"функция txTransparentBlt() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y);
}
#endif
$ bool ok = (Win32::TransparentBlt != NULL);
$ if (ok)
{
$ ok &= txGDI (!!(Win32::TransparentBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), transColor)),
destImage);
}
else
{
$ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)),
destImage);
}
return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txTransparentBlt (double xDest, double yDest, HDC sourceImage,
COLORREF transColor /*= TX_BLACK*/, double xSource /*= 0*/, double ySource /*= 0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txTransparentBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, transColor);
}
//-----------------------------------------------------------------------------------------------------------------
bool txAlphaBlend (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/)
{
// Это проверки того, правильные ли HDC вы передали в функцию.
// Не бойтесь долларов - <s>это не запрещенная валюта</s> это макросы для отладки TXLib'а.
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
// Это автоматическое определение размеров картинки (точнее, HDC источника - source) с помощью txGetExtent().
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
// Это проверка того, что картинка (или ее часть) правильно попадает в окно (точнее, HDC приемника - destination, dest).
// Если она "вылезает" из окна в любую сторону, то Win32::AlphaBlend не будет работать. Эта проверка происходит только
// в режиме отладки (когда не задан макрос NDEBUG - No Debugging, без отладки).
#if !defined (NDEBUG)
$ if (!(0 <= xSource && xSource + width <= size.x &&
0 <= ySource && ySource + height <= size.y))
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
"функция txAlphaBlend() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y);
}
#endif
// Это на случай, если параметр alpha вылезает за диапазон [0..1].
$ if (alpha < 0) alpha = 0;
$ if (alpha > 1) alpha = 1;
// Об этом см. ниже.
$ BITMAP bmap = { 0, 0, 0, 0, 0, 24 };
$ bool ok = !!Win32::GetObject (txGDI ((Win32::GetCurrentObject (sourceImage, OBJ_BITMAP)), sourceImage), sizeof (bmap), &bmap);
//
// * Структура BLENDFUNCTION *
//
// Эта структура определяет, как цвета пикселей окна (точнее, source DC) и картинки смешиваются при рисовании с учетом
// прозрачности.
//
// Параметры смешивания помещаются в группу из переменных - так называемую структуру. Переменные, входящие в структуру,
// называются компонентами структуры.
//
// В этой структуре более всех важен ее третий компонент (см. ниже), задающий прозрачность картинки.
// Если он равен нулю - то картинка будет считаться полностью прозрачной и вызов Win32::AlphaBlend ничего не нарисует.
// Если он равен 255 - то картинка скопируется в окно полностью, без эффекта прозрачности.
//
// С помощью четвертого компонента структуры система рисования Windows (Win32 GDI) узнает, надо ли учитывать альфа-канал
// в копируемой картинке или его надо игнорировать. Если в картинке фактически есть правильно построенный альфа-канал
// (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу
// AC_SRC_ALPHA.
//
// Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0.
//
// В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения
// функции txAlphaBlend().
//
// Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать
// альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы
// породите невнятный паленый код и безнадежно испортите себе карму. :((
//
// На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо.
//
// ____1____ 2 ___________3___________ _______________________4________________________
// / \ | / \ / \.
$ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) };
//
// * Вызов стандартной функции Win32::AlphaBlend() *
//
// Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend()
// вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите
// "Windows AlphaBlend function" и почитайте про ее параметры.
//
// Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не
// совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что
// эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s>
// это сделано для упрощения вызова функции txAlphaBlend().
$ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<--
{ // // vvvvv vvvvvv // <<--
$ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<==
sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<==
destImage); // ^^^^^ ^^^^^^ // <<--
} // ||||| |||||| // <<--
// См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<--
else
{
$ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)),
destImage);
$ ok = false;
}
// В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою
// аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а
// на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто.
// Хотя это и сложнее.</s>
// Но помни про паленый копипаст и карму, см. выше. Я предупредил.
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage,
double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txUseAlpha (HDC image)
{
$1 if (_TX_HDC_FAILED (image)) return NULL;
$ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP);
$ if (!bitmap) return NULL;
$ DIBSECTION dib = {};
$ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted;
$ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight };
$ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }};
$ RGBQUAD* buf = NULL;
$ bool isDIB = (dib.dsBm.bmPlanes == 1 &&
dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 &&
dib.dsBmih.biCompression == DIB_RGB_COLORS &&
dib.dsBm.bmBits);
$ if (!isDIB)
{
$ buf = new (std::nothrow) RGBQUAD [size.x * size.y];
$ if (!buf) return NULL;
$ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted;
}
else
{
$ buf = (RGBQUAD*) dib.dsBm.bmBits;
}
$ for (int y = 0; y < size.y; y++)
for (int x = 0; x < size.x; x++)
{
RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer
color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0);
color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0);
color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0);
}
$ if (!isDIB)
{
$ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted;
$ delete[] buf;
}
$ return image;
}
//-----------------------------------------------------------------------------------------------------------------
bool txSaveImage (const char filename[], HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (filename)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT size = txGetExtent (dc);
$ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER),
szImg = (size.x * size.y) * sizeof (RGBQUAD);
$ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs };
$ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB };
$ bool ok = true;
$ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y];
$ ok &= (buf != NULL);
$ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y,
buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted;
$ FILE* f = NULL;
$ if (ok) fopen_s (&f, filename, "wb");
$ ok &= (f != NULL);
$ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1);
$ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1);
$ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1);
$ ok &= (f && fclose (f) == 0);
$ delete[] buf;
$ buf = NULL;
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline void txRedrawWindow()
{
$1 txSleep (0);
}
//-----------------------------------------------------------------------------------------------------------------
inline int txUpdateWindow (int update /*= true*/)
{
$1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update);
}
//-----------------------------------------------------------------------------------------------------------------
inline int txBegin()
{
$1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1);
$ return _txCanvas_RefreshLock;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txEnd()
{
$1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1);
$ return _txCanvas_RefreshLock;
}
//-----------------------------------------------------------------------------------------------------------------
double txSleep (double time)
{
$1 LARGE_INTEGER start = {};
$ QueryPerformanceCounter (&start) asserted;
$ LARGE_INTEGER freq = {};
$ QueryPerformanceFrequency (&freq) asserted;
$ int lock = _txCanvas_RefreshLock;
$ _txCanvas_RefreshLock = 0;
$ HWND wnd = txWindow();
if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
$ Sleep (ROUND ((time >= 0)? time : 0));
$ _txCanvas_RefreshLock = lock;
$ LARGE_INTEGER stop = {};
$ QueryPerformanceCounter (&stop) asserted;
$ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart;
}
//-----------------------------------------------------------------------------------------------------------------
bool txLock (bool wait /*= true*/)
{
$0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
$ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; }
else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); }
}
//-----------------------------------------------------------------------------------------------------------------
bool txUnlock()
{
$0 LeaveCriticalSection (&_txCanvas_LockBackBuf);
$ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
inline T txUnlock (T value)
{
$1 txUnlock();
$ return value;
}
//-----------------------------------------------------------------------------------------------------------------
inline POINT txMousePos()
{
$1 POINT pos = {};
$ GetCursorPos (&pos);
$ if (txWindow())
{$ ScreenToClient (txWindow(), &pos); }
$ return pos;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txMouseX()
{
return txMousePos() .x;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txMouseY()
{
return txMousePos() .y;
}
//-----------------------------------------------------------------------------------------------------------------
inline unsigned txMouseButtons()
{
$1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0
((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1
((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/)
{
unsigned oldAttr = txGetConsoleAttr();
SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color);
return oldAttr;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txGetConsoleAttr()
{
CONSOLE_SCREEN_BUFFER_INFO con = {};
GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
return con.wAttributes;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txSetConsoleCursorPos (double x, double y)
{
$1 POINT fontSz = txGetConsoleFontSize();
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left),
(short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) };
$ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted;
$ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
$ return prev;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleCursorPos()
{
$1 POINT fontSz = txGetConsoleFontSize();
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
$ return pos;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleExtent()
{
$1 CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ POINT size = { con.srWindow.Right - con.srWindow.Left + 1,
con.srWindow.Bottom - con.srWindow.Top + 1 };
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
bool txClearConsole()
{
$1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (out, &con) asserted;
$ COORD start = {con.srWindow.Left, con.srWindow.Top};
$ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) *
(con.srWindow.Bottom - con.srWindow.Top + 1);
$ DWORD written = 0;
$ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted;
$ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted;
$ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted;
$ return written == len;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleFontSize()
{
$1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}};
$ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font));
$ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y };
$ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC());
$ POINT sizeFont = { size.cx, size.cy };
$ return sizeFont;
}
//-----------------------------------------------------------------------------------------------------------------
bool txTextCursor (bool blink /*= true*/)
{
$1 bool old = _txConsole_IsBlinking;
$ _txConsole_IsBlinking = blink;
$ return old;
}
//-----------------------------------------------------------------------------------------------------------------
bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/)
{
$1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT;
$ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC;
$ if (!filename) mode = SND_PURGE;
$ return !!Win32::PlaySound (filename, NULL, mode);
}
//-----------------------------------------------------------------------------------------------------------------
int txSpeak (const char* text, ...)
{
$1 bool verbose = false; (void) verbose;
$ bool async = false; (void) async;
$ for (; text && *text; text++)
{
if (*text == '\a') {$ async = true; }
else if (*text == '\v') {$ verbose = true; }
else break;
}
$ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!";
$ va_list arg; va_start (arg, text);
if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); }
$ va_end (arg);
#ifdef TX_USE_SPEAK
if (text && verbose) {$ printf ("%s", textA); }
$ int time = GetTickCount();
$ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L"";
$ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW));
$ static ISpVoice* voice = NULL;
$ if (text && !voice)
{
$ HRESULT res = Win32::CoInitialize (NULL);
if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); }
}
$ if (text && voice)
{
$ Win32::_fpreset();
$ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL);
$ tx_fpreset();
}
$ if (!text && voice)
{
$ voice->Release();
$ voice = NULL;
$ Win32::CoUninitialize();
}
$ return (voice)? GetTickCount() - time : -1;
#else
$ if (text)
{
$ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK);
$ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA);
$ txSetConsoleAttr (oldAttr);
}
$ return -1;
#endif
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[],
double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/)
{
$1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1;
$ int time = GetTickCount();
$ static char processUID [64] = "";
if (!*processUID)
{
$ FILETIME startTime = {}, null = {};
$ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted;
$ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo",
(unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted;
}
$ if (!fileName)
{
$ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern
$ return 0;
}
$ static const char* vlcPath = _txPlayVideo_FindVLC();
$ if (!vlcPath || _access (vlcPath, 0) != 0)
{
$ static int once = false;
$ if (*fileName && !once++)
{$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org "
"и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n"
"--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n"
"P.S. См. мое описание в TXLib Help."); }
$ return INT_MIN;
}
$ bool async = false;
if (*fileName == '\a') {$ async = true; fileName++; }
$ RECT rect = {};
if (wnd) {$ GetClientRect (wnd, &rect); }
if (!width) {$ width = rect.right; }
if (!height) {$ height = rect.bottom; }
// Create a child window to hold the video stream
$ const char* errPos = "ВНЕЗАПНО";
$ volatile HWND child = NULL;
$ if (wnd && (wnd == txWindow()))
{
$ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1);
$ static int number = 1;
$ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x,
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass };
$ child = txCreateExtraWindow (createData);
$ if (!child)
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos));
$ return INT_MIN+3;
}
$ BringWindowToTop (child);
$ wnd = child;
}
// Build the command line
if (!zoom && !wnd) {$ zoom = 1; }
$ char sZoom [64] = "--autoscale";
if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; }
$ static char cmd [MAX_PATH*2 + 1024] = "";
$ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit"
" %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s"
" --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging"
" --ignore-config --reset-config --no-one-instance --play-and-exit"
" --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file"
" --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events",
vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted;
$ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n",
x, y, width, height, fileName, zoom, gain, wnd, cmd);
$ if (!*fileName)
{
if (child) {$ txDestroyWindow (child); }
$ return (intptr_t) cmd;
}
$ if (!strstr (fileName, "://") && _access (fileName, 0) != 0)
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos));
if (child) {$ txDestroyWindow (child); }
$ return INT_MIN+1;
}
// Run VLC, run
$ PROCESS_INFORMATION vlc = {};
$ STARTUPINFO start = { sizeof (start) };
$ DWORD ret = 0;
$ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) &&
vlc.hProcess && vlc.hThread)
{
$ if (child)
{
$ assert (wnd == child);
$ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess);
}
$ if (!async)
{
$ WaitForSingleObject (vlc.hProcess, INFINITE);
$ GetExitCodeProcess (vlc.hProcess, &ret) asserted;
}
$ if (!child)
{
$ CloseHandle (vlc.hProcess) asserted;
}
$ CloseHandle (vlc.hThread) asserted;
$ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret);
}
else
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos));
$ if (child)
{$ txDestroyWindow (child); }
$ return INT_MIN+4;
}
#undef PROCESS_UID_
}
//-----------------------------------------------------------------------------------------------------------------
inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/)
{
$1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd);
}
//-----------------------------------------------------------------------------------------------------------------
LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
{
const UINT_PTR checkTimer = 1;
switch (msg)
{
case WM_CREATE:
{
$1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted;
}
break;
case WM_DESTROY:
{
$1 KillTimer (wnd, checkTimer) asserted;
$ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
$ if (vlc)
{
$ Win32::TerminateProcess (vlc, 0);
$ CloseHandle (vlc) asserted;
$ SetWindowLongPtr (wnd, GWLP_USERDATA, 0);
}
}
break;
case WM_TIMER:
{
HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT)
{
$1 DestroyWindow (wnd) asserted;
}
}
break;
default:
break;
}
return DefWindowProc (wnd, msg, wpar, lpar);
}
//-----------------------------------------------------------------------------------------------------------------
const char* _txPlayVideo_FindVLC()
{
$1 static char vlcPath [MAX_PATH] = "";
$ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath)))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath)))
{
$ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX);
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ return NULL;
}
//-----------------------------------------------------------------------------------------------------------------
// +--<<< Это вряд ли имеет отношение к тому, что вы ищете :)
// V Полезно смотреть не только вверх, но и вниз
WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/)
{
$1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc;
$ return old;
}
//-----------------------------------------------------------------------------------------------------------------
// +--<<< А это, наконец, искомое определение этой функции.
// | Смотрите по сторонам! Нужная вам функция где-то рядом.
// |
// v
bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()
{
txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n"
"txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n"
"Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. "
"Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n"
"Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом "
"с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n"
"Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы "
"установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n",
"Не получилось", MB_ICONSTOP);
// The truth is out there... (C++files)
return false;
}
//-----------------------------------------------------------------------------------------------------------------
// Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you.
inline bool txDisableAutoPause()
{
_txExit = true;
return true;
}
// P.S. This library contains more undocumented functions. Search them via "Luke" keyword.
//-----------------------------------------------------------------------------------------------------------------
void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/)
{
$1 assert (!_txIsBadReadPtr (address));
const unsigned char* p = (const unsigned char*) address;
$ unsigned x = 0;
$ unsigned attr = txGetConsoleAttr();
$ txSetConsoleAttr (FOREGROUND_WHITE);
$ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : ""));
$ txSetConsoleAttr (FOREGROUND_YELLOW);
$ for (x = 0; x < 16; x++) printf ("%02X ", x);
$ for (x = 0; x < 16; x++) printf ("%X", x);
$ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0";
$ const char xlatCh[] = "·" "·" "·" "·" "·" "·";
$ const size_t szCtrl = sizeof (isCtrl) - 1;
$ int oldCP = GetConsoleOutputCP();
$ for (int y = 0; y < 16; y++, p += 16)
{
txSetConsoleAttr (FOREGROUND_YELLOW);
printf ("\n" "%*p ", (int) sizeof (address) * 2, p);
int color = FOREGROUND_LIGHTGREEN;
for (x = 0; x < 16; x++)
{
txSetConsoleAttr (color + x/4%2);
printf ("%02X ", p[x]);
}
for (x = 0; x < 16; x++)
{
txSetConsoleAttr (color + x/4%2);
char c = p[x];
const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl);
if (ctrl) c = xlatCh [ctrl - isCtrl];
if (ctrl) SetConsoleOutputCP (1251);
printf ("%c", c);
if (ctrl) SetConsoleOutputCP (oldCP);
}
}
$ txSetConsoleAttr (attr);
$ printf ("\n");
$ if (pause)
{
$ SetConsoleOutputCP (_TX_CODEPAGE);
$ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP);
$ (void)_getch();
$ SetConsoleOutputCP (oldCP);
}
}
//-----------------------------------------------------------------------------------------------------------------
void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/,
bool readSource /*= true*/)
{
$1 unsigned attr = txGetConsoleAttr();
$ txSetConsoleAttr (FOREGROUND_LIGHTCYAN);
$ fprintf (stderr, "\n" "--------------------------------------------------\n"
"Трассировка стека из \"%s\" at %s (%d):\n\n"
"%s\n\n"
"--------------------------------------------------\n\n",
func, file, line, _txCaptureStackBackTrace (1, readSource));
$ txSetConsoleAttr (attr);
}
//-----------------------------------------------------------------------------------------------------------------
char* txDemangle (const char* mangledName, std::nomeow_t)
{
$1 if (!mangledName) return NULL;
$ char* typeName = NULL;
#if defined (_GCC_VER)
$ int err = 1;
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err;
if (typeName) {$ return typeName; }
#endif
$ unsigned short flags = 0;
$ if (mangledName[0] == '.')
{
$ mangledName++;
$ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY
}
$ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags));
if (typeName) {$ return typeName; }
$ return _strdup (mangledName);
}
//-----------------------------------------------------------------------------------------------------------------
std::string txDemangle (const char* mangledName)
{
$1 char* typeName = txDemangle (mangledName, std::nomeow);
$ std::string name (typeName? typeName : "");
$ free (typeName);
$ return name;
}
//-----------------------------------------------------------------------------------------------------------------
double txQueryPerformance()
{
$1 int maxTime = 500;
$ int maxSamples = 100;
$ POINT size = {100, 100};
$ HDC dc = _txBuffer_Create (txWindow(), &size, NULL);
$ assert (dc); if (!dc) return -1;
$ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1);
$ assert (mask);
$ LARGE_INTEGER freq = {};
$ QueryPerformanceFrequency (&freq) asserted;
$ LARGE_INTEGER start = {};
$ QueryPerformanceCounter (&start) asserted;
$ int samples = 0;
$ while (samples++ < maxSamples)
{
$ LARGE_INTEGER cur = {};
$ QueryPerformanceCounter (&cur) asserted;
$ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart;
$ if (t > maxTime) break;
// Draw test scene
$ for (int y = 0; y < size.y; y++)
for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc);
$ for (int y = 0; y < size.y; y += 10)
for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc);
$ txEllipse (0, 0, size.x, size.y, dc);
$ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc);
$ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted;
$ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted;
$ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted;
$ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted;
}
$ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask);
$ assert (mask);
$ _txBuffer_Delete (&dc);
$ return 3.0 * samples / sqrt (1.0 * size.x * size.y);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_TX_CPP11)
template <int txFramesToAverage>
#endif
double txGetFPS (int minFrames)
{
$1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0);
$ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time);
$ if (time.QuadPart - time0.QuadPart == 0)
{$ return 0; }
$ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq);
$ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart);
$ time0 = time;
$ if (txFramesToAverage == 0) return fps;
$ static double average [txFramesToAverage] = {};
$ static unsigned n = 0;
$ average [n++ % txFramesToAverage] = fps;
$ unsigned nn = MIN (n, (unsigned) sizearr (average));
$ static double median [txFramesToAverage] = {};
$ std::copy (average, average + nn, median);
$ std::nth_element (median, median + nn/2, median + nn);
$ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0;
$ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txExtractColor (COLORREF color, COLORREF component)
{
$1 switch (component)
{
case TX_RED:
case TX_HUE: $ return (color >> 0) & 0xFF;
case TX_GREEN:
case TX_SATURATION: $ return (color >> 8) & 0xFF;
case TX_BLUE:
case TX_LIGHTNESS: $ return (color >> 16) & 0xFF;
default: $ return CLR_INVALID;
}
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txRGB2HSL (COLORREF rgbColor)
{
$1 int r = (int) txExtractColor (rgbColor, TX_RED),
g = (int) txExtractColor (rgbColor, TX_GREEN),
b = (int) txExtractColor (rgbColor, TX_BLUE);
$ double m1 = MAX (MAX (r, g), b) / 255.0,
m2 = MIN (MIN (r, g), b) / 255.0,
dm = m1 - m2,
sm = m1 + m2,
ir = r / 255.0,
ig = g / 255.0,
ib = b / 255.0,
ih = 0,
is = 0,
il = sm / 2;
$ const double prec = 0.001;
$ if (fabs (dm) < prec)
{
$ is = dm / ((sm <= 1)? sm : (2-sm));
$ double cr = (m1 - ir) / dm,
cg = (m1 - ig) / dm,
cb = (m1 - ib) / dm;
$ if (fabs (ir - m1) < prec) ih = cb - cg;
$ if (fabs (ig - m1) < prec) ih = 2 + cr - cb;
$ if (fabs (ib - m1) < prec) ih = 4 + cg - cr;
}
$ ih = (ih >= 0)? ih*60 : ih*60 + 360;
$ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255));
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txHSL2RGB (COLORREF hslColor)
{
$1 struct xRGB
{
static double calc (double h, double m1, double m2)
{
$ if (h < 0) h += 360;
$ if (h > 360) h -= 360;
$ return (h < 60)? m1 + (m2-m1) * h / 60 :
(h < 180)? m2 :
(h < 240)? m1 + (m2-m1) * (240-h) / 60 :
m1;
}
};
$ int h = (int) txExtractColor (hslColor, TX_HUE),
s = (int) txExtractColor (hslColor, TX_SATURATION),
l = (int) txExtractColor (hslColor, TX_LIGHTNESS);
$ double ih = h / 255.0 * 360.0,
il = l / 100.0,
is = s / 100.0,
m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is,
m1 = 2 * il - m2,
ir = s? xRGB::calc (ih + 120, m1, m2) : il,
ig = s? xRGB::calc (ih, m1, m2) : il,
ib = s? xRGB::calc (ih - 120, m1, m2) : il;
$ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255));
}
//-----------------------------------------------------------------------------------------------------------------
inline double random (std::nomeow_t, double left, double right)
{
return left + (right - left) * ((double) rand() / RAND_MAX);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename Tx, typename Ta, typename Tb>
inline bool In (std::nomeow_t, Tx x, Ta a, Tb b)
{
return a <= x && x <= b;
}
//-----------------------------------------------------------------------------------------------------------------
inline int random (int range)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return rand() % range;
}
//-----------------------------------------------------------------------------------------------------------------
inline double random (double left, double right)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return random (std::nomeow, left, right);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename Tx, typename Ta, typename Tb>
inline bool In (Tx x, Ta a, Tb b)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return In (std::nomeow, x, a, b);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool In (const POINT& pt, const RECT& rect)
{
if (_TX_ARGUMENT_FAILED (&pt)) return false;
if (_TX_ARGUMENT_FAILED (&rect)) return false;
return In (std::nomeow, pt.x, rect.left, rect.right) &&
In (std::nomeow, pt.y, rect.top, rect.bottom);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool In (const COORD& pt, const SMALL_RECT& rect)
{
if (_TX_ARGUMENT_FAILED (&pt)) return false;
if (_TX_ARGUMENT_FAILED (&rect)) return false;
return In (std::nomeow, pt.X, rect.Left, rect.Right) &&
In (std::nomeow, pt.Y, rect.Top, rect.Bottom);
}
//-----------------------------------------------------------------------------------------------------------------
void tx_fpreset()
{
$1 txAutoLock _lock;
$ Win32::_fpreset();
$ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW
#if !defined (__CYGWIN__)
$ unsigned old87 = 0;
$ if (_controlfp_s (&old87, 0, 0) == 0)
{$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM
#else
$ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM
#endif
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
inline T zero() { T __zero = {}; return __zero; }
//}
//=================================================================================================================
//=================================================================================================================
//{ txPrintf() implementation
// Реализация txPrintf()
//=================================================================================================================
#if defined (_TX_CPP11)
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args);
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt);
template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg);
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg);
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt);
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args)
{
$1 assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%') {$}
else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); }
else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); }
else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); }
else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt)
{
$1 assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (!fmt[0]) {$}
else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); }
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt)
{
$1 assert (&stream);
$ assert (fmt);
$ while (*fmt)
{
if (fmt[0] == '%')
{
if (fmt[1] == '%') fmt++;
else break;
}
stream << *fmt++;
}
$ }
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg)
{
$1 assert (&stream);
$ assert (fmt);
$ if (_TX_ARGUMENT_FAILED (&arg)) return;
if (fmt[0] == '%') {$}
else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); }
$ fmt++;
$ char oldFill = stream.fill (' ');
$ std::ios_base::fmtflags oldFlags = stream.flags();
$ for (;;) switch (*fmt)
{
case '-': $ stream << std::left; fmt++; break;
case '+': $ stream << std::showpos; fmt++; break;
case ' ': $ stream.fill (' '); fmt++; break;
case '#': $ stream << std::showbase; fmt++; break;
case '0': $ stream.fill ('0'); fmt++; break;
default: $ goto end;
}
end:
$ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0);
$ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0;
if (width) {$ stream << std::setw (width); }
if (prec) {$ stream << std::setprecision (prec); }
$ fmt += strspn (fmt, "hljztL");
$ switch (*fmt)
{
case '$':
case '?': $ break;
case 'd':
case 'i':
case 'u': $ stream << std::dec; break;
case 'o': $ stream << std::oct; break;
case 'x': $ stream << std::hex; break;
case 'X': $ stream << std::hex << std::uppercase; break;
case 'f': $ stream << std::fixed; break;
case 'F': $ stream << std::fixed << std::uppercase; break;
case 'e': $ stream << std::scientific; break;
case 'E': $ stream << std::scientific << std::uppercase; break;
case 'g': $ break;
case 'G': $ stream << std::uppercase; break;
case 'a': $ break;
case 'A': $ stream << std::uppercase; break;
case 'c':
case 's':
case 'p': $ break;
default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break;
}
$ fmt++;
if (&arg) {$ stream << arg; }
else {$ stream << "(null)"; }
$ stream.fill (oldFill);
$ stream.flags (oldFlags);
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg)
{
$1 assert (fmt);
if (_TX_ARGUMENT_FAILED (arg)) return;
if (fmt[0] == '%' && fmt[1] == 'n') {$}
else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); }
$ *arg = (int) stream.str().length();
$ fmt += 2;
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; }
inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); }
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&stream)) return 0;
$ if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ const char* fmt = format;
$ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...);
$ return (int) stream.str().length();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0;
$ if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ if (size > 0) size--;
$ buffer[size] = 0;
$ if (!size) return 0;
$ std::ostringstream stream;
$ stream.rdbuf() -> pubsetbuf (buffer, size);
$ txPrintf (stream, format, args...);
$ return (int) stream.str().length();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline std::string txFormat (const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&format)) return "";
$ std::ostringstream stream;
$ txPrintf (stream, format, args...);
$ return stream.str();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ return printf ("%s", txFormat (format, args...) .c_str());
}
#endif
//-----------------------------------------------------------------------------------------------------------------
int _txPrintfCheck (const char* format, ...) tx_printfy (1);
inline int _txPrintfCheck (const char*, ...) { return 0; }
//}
//=================================================================================================================
//=================================================================================================================
//{ txDialog methods implementation
// Реализация методов класса txDialog
//
// See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx
// [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743
// [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753
//=================================================================================================================
txDialog::txDialog () :
layout_ (NULL)
{$1}
//-----------------------------------------------------------------------------------------------------------------
txDialog::txDialog (const Layout* layout) :
layout_ (layout)
{$1}
//-----------------------------------------------------------------------------------------------------------------
const txDialog::Layout* txDialog::setLayout (const Layout* layout)
{
$1 assert (layout);
$ return ::std::swap (layout_, layout), layout;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txDialog::dialogBox (WORD resourceID)
{
$1 const char* resName = (char*)(uintptr_t) resourceID;
$ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0;
$ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/)
{
$1 if (!layout) layout = layout_;
$ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0;
$ if (!bufsize) bufsize = 1024;
$ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize);
$ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0;
$ const Layout* dlg = &layout[0];
$ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 };
$ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize,
(dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0,
dlg->x, dlg->y, dlg->sx, dlg->sy,
dlg->caption? dlg->caption : def.caption,
dlg->font? dlg->font : def.font,
dlg->fontsize? dlg->fontsize : def.fontsize, NULL);
$ WORD i = 0;
$ for (i = 1; layout[i].wndclass != END; ++i)
{
$ const Layout* item = &layout[i];
$ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl),
item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy,
item->id, (const char*)(uintptr_t) item->wndclass, item->caption);
}
$ tmpl->cdit = (unsigned short) (i-1);
$ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
$ GlobalFree (tmpl);
$ return res;
}
//-----------------------------------------------------------------------------------------------------------------
int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM)
{
$1 switch (msg)
{
case WM_INITDIALOG: $ SetForegroundWindow (wnd);
$ break;
case WM_COMMAND: $ switch (LOWORD (wParam))
{
case IDOK:
case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow());
$ EndDialog (wnd, (uintptr_t) this);
$ break;
default: $ break;
}
$ break;
default: $ break;
}
$ return FALSE;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
$1 static txDialog* this__ = NULL;
$ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam;
$ if (!this__) return FALSE;
$ return this__-> dialogProc (wnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle,
WORD controls, short x, short y, short cx, short cy,
const char caption[], const char font[], WORD fontsize, const char menu[])
{
$1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL;
$ WORD* pw = (WORD*) globalMem;
$ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->cdit = controls;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ if (menu > (const char*) 0xFFFF)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
else
{
$ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0);
$ *pw++ = (WORD)(uintptr_t) menu;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ if (style & DS_SETFONT)
{
$ *pw++ = fontsize;
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ return pw;
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle,
short x, short y, short cx, short cy,
WORD id, const char wclass[], const char caption[])
{
$1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL;
$ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary
$ ((ULONG&) pw) += 3;
$ ((ULONG&) pw) >>= 2;
$ ((ULONG&) pw) <<= 2;
$ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ tmpl->id = id;
$ if (HIWORD (wclass) == 0xFFFF)
{
$ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass));
$ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass));
}
else if (wclass)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ *pw++ = 0;
$ return pw;
}
//}
//=================================================================================================================
//=================================================================================================================
//{ Cleaning up the utility macros
// Очистка служебных макросов
//=================================================================================================================
#undef $
#undef $0
#undef $1
#undef $2
#undef $3
#undef $4
#undef $5
#undef $6
#undef $7
#undef $8
#undef $9
#undef $$
//}
//=================================================================================================================
//! @endcond
//=================================================================================================================
//{ Experimental Debugging macros
//! @name Экспериментальные отладочные макросы
//=================================================================================================================
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Misc
//! @brief Отладочная печать переменной во время вычисления выражения или участка кода
//! во время его выполнения.
//!
//! Сделай приятными твои <i>круглые сутки : "ВНЕЗАПНО"),
11627 VPRINT_ (msg, args);
11629 while (s > what && s[-1] ==
'\n') s--;
11631 PRINT_ (
"\n\n" "#%d: %s, Instance: 0x%p (%d-bit), Flags: %c%c%c%c%c%d, Thread: 0x%X%s",
11633 _txErrors,
_TX_VERSION, &_txInitialized, (
sizeof (
void*) == 4)? 32 : 64,
11635 "cC"[_txConsole],
"mM"[_txMain],
"dD"[_txIsDll],
"rR"[_txRunning],
"eE"[_txExit], _txLoc::Cur.trace,
11637 threadId, (threadId == _txMainThreadId)?
" (Main)" :
11638 (threadId == _txCanvas_ThreadId)?
" (Canvas)" :
"");
11640 if (winErr) PRINT_ (
", GetLastError(): %lu (", (
unsigned long) winErr),
11641 s += FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
11642 NULL, winErr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
11643 s, (DWORD) (
sizeof (what) - (s-what)), NULL) - 2,
11644 s -= (s[-1] ==
'.')? 1 : 0,
11647 if (crtErr) PRINT_ (
", errno: %d (%s)", crtErr, (
strerror_s (str,
sizeof (str), crtErr), str));
11648 if (dosErr) PRINT_ (
", _doserrno: %lu (%s)", dosErr, (
strerror_s (str,
sizeof (str), dosErr), str));
11649 if (oglErr) PRINT_ (
", glGetError(): %u (0x%04X, %s)", oglErr, oglErr, _TX_CALL (Win32::gluErrorString, (oglErr)));
11651 #if (__cplusplus <= 201703L)
11652 PRINT_ (
". %s\n", ::std::uncaught_exception ()?
"std::uncaught_exception(): true." :
"");
11654 PRINT_ (
". %s\n", ::std::uncaught_exceptions()?
"std::uncaught_exceptions(): true." :
"");
11657 if (_txLoc::Cur.inTX > 0 && file && !(_txLoc::Cur.line == line && _stricmp (_txLoc::Cur.file, file) == 0) &&
11658 (_txLoc::Cur.file || _txLoc::Cur.line || _txLoc::Cur.func))
11659 PRINT_ (
"From: %s (%d) %s.\n", _txLoc::Cur.file, _txLoc::Cur.line, _txLoc::Cur.func);
11663 if (options & fmtOnly)
11665 SetLastError (winErr);
11669 #if !defined (__CYGWIN__)
11670 _doserrno = dosErr;
11677 txSetConsoleAttr ((options & isFatal)? FOREGROUND_LIGHTMAGENTA : FOREGROUND_LIGHTRED);
11682 fprintf (stderr,
"\n" "--------------------------------------------------\n"
11684 "--------------------------------------------------\n",
11687 if (stkTrace && strstr (stkTrace,
".exe: "))
11688 {$ fprintf (stderr,
"Стек вызовов:\n\n"
11690 "--------------------------------------------------\n",
11693 SetConsoleOutputCP (oldCodePage);
11699 if (!log) {$
break; }
11701 fprintf (log,
"\n" "--------------------------------------------------\n"
11703 "--------------------------------------------------\n",
11706 fprintf (log,
"Стек вызовов:\n\n"
11708 (*_txTraceSE? _txTraceSE : stkTrace));
11710 #if defined (_TX_ALLOW_TRACE) || defined (_DEBUG)
11712 if (_txLoc::Cur.inTX > 0 && txTrace && *txTrace)
11714 fprintf (log,
"\n" "--------------------------------------------------\n"
11715 "Стек вызовов TX:\n\n"
11722 fprintf (log,
"\n" "--------------------------------------------------\n"
11724 "--------------------------------------------------\n",
11733 if (!(options & noMsgBox))
11737 PRINT_ (
"\n" "Прервать программу?");
11739 ret =
txMessageBox (what, ((options & isWarning)?
"Предупреждение" :
11740 (options & isFatal)?
"Фатальная ошибка" :
11741 "Ошибка в программе ), MB_ICONSTOP | MB_SYSTEMMODAL | MB_YESNOCANCEL);
}
SetLastError (winErr);
errno = crtErr;
#if !defined (__CYGWIN__)
_doserrno = dosErr;
#endif
if (((options & isFatal) && !IsDebuggerPresent()) || ret == IDYES)
{
txUnlock();
_txCleanup();
Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
}
#undef PRINT_
return what;
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_MSC_VER)
int _txOnErrorReport (int type, const char* text, int* ret)
{
assert (text);
assert (ret);
_txErrors++;
unsigned restore = txGetConsoleAttr();
switch (type)
{
case _CRT_WARN: txSetConsoleAttr (FOREGROUND_LIGHTRED); break;
case _CRT_ERROR: txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA); break;
case _CRT_ASSERT: txSetConsoleAttr (FOREGROUND_YELLOW); break;
default: break;
}
const char startReport[] = "Detected memory leaks!\n",
endReport[] = "Object dump complete.\n";
if (strcmp (text, startReport) == 0) // Dirty, dirty hack. А что делать?
{
_txOnErrorReport (type, "\n", NULL);
_txOnErrorReport (type, _TX_VERSION " - ERROR: ", NULL);
_txOnErrorReport (type, "Внимание: Обнаружены утечки памяти! (Для поиска используйте _TX_ALLOC_BREAK.)\n", NULL);
_txOnErrorReport (type, "\n", NULL);
}
size_t len = strlen (text);
if (text [len-1] != '\n') txOutputDebugPrintf ("%s", text);
else if (strcmp (text, endReport) != 0) txOutputDebugPrintf ("%s" "%s - ERROR: ", text, _TX_VERSION);
else txOutputDebugPrintf ("%s\n", text);
DWORD n = 0;
HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
WriteFile (err, text, (DWORD) strlen (text), &n, NULL);
txSetConsoleAttr (restore);
if (*_txLogName) do
{
HANDLE log = CreateFile (_txLogName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (log == INVALID_HANDLE_VALUE) break;
SetFilePointer (log, 0, NULL, FILE_END);
WriteFile (log, text, (DWORD) strlen (text), &n, NULL);
CloseHandle (log);
break;
}
while (false);
if (ret) *ret = 0;
return (type == _CRT_WARN);
}
#endif
//-----------------------------------------------------------------------------------------------------------------
int txMessageBox (const char text[], const char header[], unsigned flags /*= MB_ICONINFORMATION | MB_OKCANCEL*/)
{
$5 static wchar_t textW [_TX_BIGBUFSIZE * sizeof (wchar_t)] = L"[NULL text]";
$ static wchar_t headerW [_TX_BUFSIZE * sizeof (wchar_t)] = L"[NULL header]";
if (text) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, text, -1, textW, sizearr (textW)) || memset (textW, 0, sizeof (textW)); }
if (header) {$ MultiByteToWideChar (_TX_CODEPAGE, 0, header, -1, headerW, sizearr (headerW)) || memset (headerW, 0, sizeof (headerW)); }
$ HWND wnd = _txCanvas_Window;
$ int ret = MessageBoxW ((wnd && IsWindowVisible (wnd))? wnd : _TX_CALL (Win32::GetConsoleWindow,()),
textW, headerW, flags | MB_SETFOREGROUND | MB_TOPMOST | MB_OKCANCEL );
$ if (ret == IDCANCEL)
{
$ SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0);
$ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT);
}
$ return ret;
}
//-----------------------------------------------------------------------------------------------------------------
bool txGetAsyncKeyState (int key)
{
$1 HWND wnd = GetForegroundWindow();
return (GetAsyncKeyState (key) & 0x8000) &&
(wnd == txWindow() || wnd == Win32::GetConsoleWindow());
}
//-----------------------------------------------------------------------------------------------------------------
bool txNotifyIcon (unsigned flags, const char title[], const char format[], ...)
{
$5 if (_TX_ARGUMENT_FAILED (format)) return false;
$ va_list arg; va_start (arg, format);
$ bool ok = true;
#if defined (_WIN32_IE) && (_WIN32_IE >= 0x0500)
$ NOTIFYICONDATA nid = { sizeof (nid) };
$ nid.uFlags = NIF_ICON | NIF_TIP | NIF_INFO;
$ nid.hWnd = NULL;
$ nid.uID = 1;
$ nid.hIcon = _txCreateTXIcon (16); assert (nid.hIcon);
$ strncpy_s (nid.szTip, sizeof (nid.szTip), "TXLib Information", sizeof (nid.szTip));
$ strncpy_s (nid.szInfoTitle, sizeof (nid.szInfoTitle), (title? title : "TXLib сообщает"), sizeof (nid.szInfoTitle) - 1);
$ _tx_vsnprintf_s (nid.szInfo, sizeof (nid.szInfo), format, arg);
$ nid.dwInfoFlags = flags;
$ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification)\n", nid.szInfoTitle, nid.szInfo);
$ ok &= !!Shell_NotifyIcon (NIM_ADD, (::NOTIFYICONDATA*) &nid);
$ ok &= !!Shell_NotifyIcon (NIM_MODIFY, (::NOTIFYICONDATA*) &nid);
$ if (nid.hIcon) DestroyIcon (nid.hIcon) asserted;
#else
$ char nid_szInfo[_TX_BUFSIZE] = "";
$ _tx_vsnprintf_s (nid_szInfo, sizeof (nid_szInfo), format, arg);
$ txOutputDebugPrintf ("\r" _TX_VERSION " - %s: %s (Icon notification - NOT displayed)\n", title, nid_szInfo);
$ ok = false;
$ (void)flags; (void)title;
#endif
$ va_end (arg);
return ok;
}
//-----------------------------------------------------------------------------------------------------------------
void _txTrace (const char file[], int line, const char func[], const char msg[] /*= NULL*/, ...)
{
unsigned id = GetCurrentThreadId();
const char marks[2][2][3] = {{"uU", "cC"}, {"mM", "??"}};
char mark = marks [id == _txMainThreadId] [id == _txCanvas_ThreadId] [(_txLoc::Cur.inTX > 0)];
char msgStr[_TX_BUFSIZE] = "";
if (msg)
{
va_list arg; va_start (arg, msg);
_tx_vsnprintf_s (msgStr, sizeof (msgStr) - 1, msg, arg);
va_end (arg);
}
txOutputDebugPrintf ("%s - 0x%p %c%c%c%c%c%d [%c] - %-*s (%5d) " "|%*s%s" "%s%s\n",
_TX_VERSION, &_txInitialized,
"cC"[_txConsole], "mM"[_txMain], "dD"[_txIsDll], "rR"[_txRunning], "eE"[_txExit],
_txLoc::Cur.trace, mark,
(int) sizeof (__FILE__) - 1, (file? file : "(NULL file)"), line,
2 * (_txLoc::Cur.inTX - 1) * !!func, "", (func? func : ""),
((*msgStr && func)? ": " : ""), msgStr);
}
//-----------------------------------------------------------------------------------------------------------------
int txOutputDebugPrintf (const char format[], ...)
{
if (!format) return 0;
enum { msgbox = 1, print = 2, compr = 4 };
int options = 0;
for (; format && *format; format++)
{
if (*format == '\a') options |= msgbox;
else if (*format == '\f') options |= print;
else if (*format == '\r') options |= compr;
else break;
}
char text[_TX_BIGBUFSIZE] = "";
va_list arg; va_start (arg, format);
int n = (int) _tx_vsnprintf_s (text, sizeof (text) - 1-1, format, arg);
va_end (arg);
struct __ { static int trimSpaces (char str[])
{
char *dst = str, *src = str;
for (char d = ' '; d; src++)
if (isspace ((unsigned char)(*src))) { if (d != ' ') *dst++ = d = ' '; }
else *dst++ = d = *src;
return (int) (dst - str - 1);
}};
if (options & compr) n = __::trimSpaces (text);
OutputDebugString (text);
if (options & print) fprintf (stderr, "%s", text);
if (options & msgbox) txMessageBox (text, "Оказывается, что", MB_ICONEXCLAMATION);
return n;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...)
{
if (!format) return 0;
va_list arg; va_start (arg, format);
intptr_t ret = _tx_vsnprintf_s (stream, size, format, arg);
va_end (arg);
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg)
{
if (!stream || !format) return 0;
#if defined (_TRUNCATE)
intptr_t ret = _vsnprintf_s (stream, size, _TRUNCATE, format, arg);
#else
intptr_t ret = _vsnprintf (stream, size, format, arg);
#endif
if (ret < 0 && size >= 4)
{
const char ellipsis[] = "...";
size_t szEllipsis = sizeof (ellipsis) - 1;
strncpy_s (stream + size - szEllipsis, szEllipsis+1, ellipsis, szEllipsis);
}
return (ret >= 0)? ret : size;
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (__CYGWIN__)
int _getch()
{
termios oldattr = {}; tcgetattr (STDIN_FILENO, &oldattr);
termios newattr = oldattr;
newattr.c_lflag &= ~(ICANON | ECHO);
tcsetattr (STDIN_FILENO, TCSANOW, &newattr);
int ch = getchar();
tcsetattr (STDIN_FILENO, TCSANOW, &oldattr);
return ch;
}
//-----------------------------------------------------------------------------------------------------------------
int _putch (int ch)
{
termios old = {}; tcgetattr (STDOUT_FILENO, &old);
termios cur = old;
cur.c_lflag &= ~ICANON;
cur.c_lflag |= ECHO;
tcsetattr (STDOUT_FILENO, TCSANOW, &cur);
putchar (ch);
tcsetattr (STDOUT_FILENO, TCSANOW, &old);
return ch;
}
//-----------------------------------------------------------------------------------------------------------------
int _kbhit()
{
termios old = {}; tcgetattr (STDIN_FILENO, &old);
termios cur = old;
cur.c_lflag &= ~(ICANON | ECHO);
cur.c_cc[VMIN] = 1;
cur.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &cur);
fd_set fd = {}; FD_SET (STDIN_FILENO, &fd);
timeval tv = {};
int res = select (STDIN_FILENO + 1, &fd, NULL, NULL, &tv) && FD_ISSET (STDIN_FILENO, &fd);
tcsetattr (STDIN_FILENO, TCSAFLUSH, &old);
return res;
}
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Information
//-----------------------------------------------------------------------------------------------------------------
const char* txGetModuleFileName (bool fileNameOnly /*= true*/)
{
static char name[MAX_PATH] = "";
if (!*name)
{
if (!GetModuleFileName (NULL, name, sizeof (name) - 1)) *name = 0;
char* ext = strrchr (name, '.');
if (ext) _strlwr_s (ext, sizeof (name) - 1 - (ext - name));
}
assert (*name);
if (fileNameOnly) return name;
static char fullName[MAX_PATH] = "";
strncpy_s (fullName, sizeof (fullName), name, sizeof (fullName) - 1);
char* title = strrchr (fullName, '\\'); if (!title) title = fullName;
char* ext = strrchr (fullName, '.'); if (!ext) ext = fullName + strlen (fullName);
size_t sz = sizeof (fullName) - (ext - fullName);
strncpy_s (ext, sz-1, " - TXLib", sz);
return title + 1;
}
//-----------------------------------------------------------------------------------------------------------------
const char* _txAppInfo()
{
$1 time_t timeT = time (NULL) - clock()/CLOCKS_PER_SEC;
char timeS[32] = "";
ctime_s (timeS, sizeof (timeS), &timeT);
static char text[_TX_BUFSIZE] = "";
char cwd [MAX_PATH] = "";
_tx_snprintf_s (text, sizeof (text) - 1,
"Developed with:\n\n"
"The Dumb Artist Library (TX Library)\n"
_TX_VERSION "\n" _TX_AUTHOR "\n"
"See license on: http://txlib.ru\n\n"
"TXLib file:" "\t" __FILE__ "\n"
"Compiled:" "\t" __DATE__ " " __TIME__ ", " __TX_COMPILER__ ", %s, %d-bit, " _TX_BUILDMODE "\n"
"Started:" "\t" "%.6s %.4s %.8s\n\n"
"Run file:" "\t" "%s\n"
"Directory:" "\t" "%s",
#if defined (_MSC_VER)
"MSVC Runtime",
#elif defined (__CYGWIN__)
"Cygwin Runtime",
#elif defined (_GCC_VER) && defined (_WIN64)
__mingw_get_crt_info(),
#else
"MinGW Runtime " TX_QUOTE (__MINGW32_MAJOR_VERSION) "." TX_QUOTE (__MINGW32_MINOR_VERSION),
#endif
(sizeof (void*) == sizeof (DWORD))? 32 : 64,
timeS + 4, timeS + 20, timeS + 11, // These offsets are ANSI standardized
txGetModuleFileName(),
_getcwd (cwd, sizeof (cwd) - 1));
return text;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//! @}
//}
//=================================================================================================================
//=================================================================================================================
//{ TXLib API implementation
// Реализация TXLib API
//=================================================================================================================
inline const char* txVersion()
{
return _TX_VERSION;
}
//-----------------------------------------------------------------------------------------------------------------
inline unsigned txVersionNumber()
{
return _TX_VER;
}
//-----------------------------------------------------------------------------------------------------------------
inline HWND txWindow()
{
$0 return _txCanvas_Window;
}
//-----------------------------------------------------------------------------------------------------------------
inline HDC& txDC()
{
$0 return _txCanvas_BackBuf[0];
}
//-----------------------------------------------------------------------------------------------------------------
inline RGBQUAD* txVideoMemory()
{
return _txCanvas_Pixels;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetExtent (HDC dc /*= txDC()*/)
{
$0 static POINT err = {-1, -1};
if (!dc) {$ POINT screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; return screen; };
if (_TX_DEFAULT_HDC_FAILED (dc)) {$ return err; }
$ BITMAP bmap = {};
$ txGDI (Win32::GetObject (Win32::GetCurrentObject (dc, OBJ_BITMAP), sizeof (bmap), &bmap), dc) asserted;
$ POINT size = { bmap.bmWidth, bmap.bmHeight };
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetExtentX (HDC dc /*= txDC()*/)
{
return txGetExtent (dc) .x;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetExtentY (HDC dc /*= txDC()*/)
{
return txGetExtent (dc) .y;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDestroyWindow (HWND wnd /*= txWindow()*/)
{
$1 if (!wnd || !txWindow()) return false;
$ if (wnd != txWindow())
{
$ return !!SendNotifyMessage (txWindow(), _TX_WM_DESTROYWND, 0, (LPARAM) wnd);
}
$ if (SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0) == 0) return false;
$ if (_txMain)
{
$ txNotifyIcon (NIIF_WARNING, NULL, "\n" "Очень, очень плохо завершать программу через txDestroyWindow().\n\n"
"Возвращайтесь через main(), там вам будут рады.\n");
$ Sleep (_TX_TIMEOUT);
}
$ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT);
$ return _txCanvas_Window == NULL;
}
//-----------------------------------------------------------------------------------------------------------------
HPEN txSetColor (COLORREF color, double thickness /*= 1*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HPEN pen = Win32::CreatePen ((color == TX_TRANSPARENT? PS_NULL : PS_SOLID), ROUND (thickness), color);
$ if (!pen) return (HPEN) NULL;
$ if (!_txBuffer_Select (pen, dc))
{
$ Win32::DeleteObject (pen);
$ return (HPEN) NULL;
}
$ if (txGDI (Win32::SetTextColor (dc, color), dc) == CLR_INVALID)
{$ return (HPEN) NULL; }
$ return pen;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txColor (double red, double green, double blue)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255));
$ return txSetColor (color)? color : CLR_INVALID;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txGetColor (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_PEN)), dc);
$ assert (obj); if (!obj) return CLR_INVALID;
$ union { EXTLOGPEN extLogPen; LOGPEN LogPen; } buf = {};
$ int size = Win32::GetObject (obj, 0, NULL);
$ Win32::GetObject (obj, sizeof (buf), &buf) asserted;
$ return (size == sizeof (LOGPEN))? buf.LogPen.lopnColor : buf.extLogPen.elpColor;
}
//-----------------------------------------------------------------------------------------------------------------
HBRUSH txSetFillColor (COLORREF color, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HBRUSH brush = (color == TX_TRANSPARENT)? (HBRUSH) Win32::GetStockObject (HOLLOW_BRUSH) : Win32::CreateSolidBrush (color);
$ return (_txBuffer_Select (brush, dc))? brush : (Win32::DeleteObject (brush), (HBRUSH) NULL);
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txFillColor (double red, double green, double blue)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255));
$ return txSetFillColor (color)? color : CLR_INVALID;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txGetFillColor (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_BRUSH)), dc);
$ assert (obj); if (!obj) return CLR_INVALID;
$ LOGBRUSH buf = {};
$ txGDI ((Win32::GetObject (obj, sizeof (buf), &buf)), dc) asserted;
$ return buf.lbColor;
}
//-----------------------------------------------------------------------------------------------------------------
bool txClear (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT size = txGetExtent (dc);
$ return txGDI (!!(Win32::PatBlt (dc, 0, 0, size.x, size.y, PATCOPY)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txSetPixel (double x, double y, COLORREF color, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ txGDI ((Win32::SetPixel (dc, ROUND (x), ROUND (y), color)), dc);
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txPixel (double x, double y, double red, double green, double blue, HDC dc /*= txDC()*/)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ return txSetPixel (x, y, RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
inline COLORREF txGetPixel (double x, double y, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ return txGDI ((Win32::GetPixel (dc, ROUND (x), ROUND (y))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txLine (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ bool ok = txGDI ((Win32::MoveToEx (dc, ROUND (x0), ROUND (y0), NULL)), dc);
$ ok &= txGDI ((Win32::LineTo (dc, ROUND (x1), ROUND (y1) )), dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txRectangle (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::Rectangle (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txPolygon (const POINT points[], int numPoints, HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (points)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI (!!(Win32::Polygon (dc, points, numPoints)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txEllipse (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::Ellipse (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txCircle (double x, double y, double r)
{
$1 return txEllipse (x-r, y-r, x+r, y+r);
}
//-----------------------------------------------------------------------------------------------------------------
bool txArc (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Arc (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txPie (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Pie (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txChord (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Chord (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txFloodFill (double x, double y,
COLORREF color /*= TX_TRANSPARENT*/, DWORD mode /*= FLOODFILLSURFACE*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ if (color == TX_TRANSPARENT) color = txGetPixel (x, y, dc);
$ return txGDI (!!(Win32::ExtFloodFill (dc, ROUND (x), ROUND (y), color, mode)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txTextOut (double x, double y, const char text[], HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (text)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ int len = (int) strlen (text);
$ bool ok = txGDI (!!(Win32::TextOut (dc, ROUND (x), ROUND (y), text, len)), dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDrawText (double x0, double y0, double x1, double y1, const char text[],
unsigned format /*= DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS*/,
HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (text)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
#if !defined (NDEBUG)
$ if (x0 > x1)
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Параметр x0 = %lg больше, чем x1 = %lg. Текст выводиться не будет.", x0, x1);
}
$ if (y0 > y1)
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Параметр y0 = %lg больше, чем y1 = %lg. Текст выводиться не будет.", y0, y1);
}
#endif
$ RECT r = { ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1) };
$ if (!strchr (text, '\n')) format |= DT_SINGLELINE;
$ unsigned prev = txSetTextAlign (TA_LEFT | TA_TOP | TA_NOUPDATECP, dc);
$ bool ok = false;
$ if (Win32::DrawText)
{
$ ok = !!txGDI ((Win32::DrawText (dc, text, -1, &r, format)), dc);
$ Win32::GetPixel (dc, 0, 0);
$ ok = true;
}
else
{
$ txTextOut ((x0 + x1) / 2, (y0 + y1) / 2, text);
$ ok = false;
}
$ txSetTextAlign (prev, dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
HFONT txSelectFont (const char name[], double sizeY, double sizeX /*= -1*/,
int bold /*= FW_DONTCARE*/, bool italic /*= false*/, bool underline /*= false*/,
bool strikeout /*= false*/, double angle /*= 0*/,
HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (name)) return NULL;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HFONT font = txFontExist (name)?
Win32::CreateFont (ROUND (sizeY), ROUND ((sizeX >= 0)? sizeX : sizeY/3),
ROUND (angle*10), 0, bold, italic, underline, strikeout,
RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, name)
:
(HFONT) Win32::GetStockObject (SYSTEM_FIXED_FONT);
$ _txBuffer_Select (font, dc);
$ return font;
}
//-----------------------------------------------------------------------------------------------------------------
SIZE txGetTextExtent (const char text[], HDC dc /*= txDC()*/)
{
$1 SIZE size = {-1, -1};
$ if (_TX_ARGUMENT_FAILED (text)) return size;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return size;
$ size_t len = strlen (text);
$ txGDI ((Win32::GetTextExtentPoint32 (dc, text, (int) len, &size)), dc) asserted;
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetTextExtentX (const char text[], HDC dc /*= txDC()*/)
{
$1 return txGetTextExtent (text, dc) .cx;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetTextExtentY (const char text[], HDC dc /*= txDC()*/)
{
$1 return txGetTextExtent (text, dc) .cy;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txSetTextAlign (unsigned align /*= TA_CENTER | TA_BASELINE*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::SetTextAlign (dc, align)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
LOGFONT* txFontExist (const char name[])
{
$1 if (_TX_ARGUMENT_FAILED (name)) return NULL;
$ static LOGFONT font = {};
$ font.lfCharSet = DEFAULT_CHARSET;
$ strncpy_s (font.lfFaceName, sizeof (font.lfFaceName), name, sizeof (font.lfFaceName) - 1);
$ struct tools
{
static int CALLBACK enumFonts (const LOGFONT* fnt, const TEXTMETRIC*, DWORD, LPARAM data)
{
$ if (_TX_ARGUMENT_FAILED (fnt)) return 0;
$ if (_TX_ARGUMENT_FAILED (data)) return 0;
#ifndef __STRICT_ANSI__
$ return _strnicmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
#else
$ return strncmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
#endif
}
};
$ return txGDI ((Win32::EnumFontFamiliesEx (NULL, &font, tools::enumFonts, (LPARAM) &font, 0)), NULL) == 0? &font : NULL;
}
//-----------------------------------------------------------------------------------------------------------------
bool txSelectObject (HGDIOBJ obj, HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (obj)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return _txBuffer_Select (obj, dc);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateCompatibleDC (double sizeX, double sizeY, HBITMAP bitmap /*= NULL*/)
{
$1 POINT size = { ROUND (sizeX), ROUND (sizeY) };
$ HDC dc = _txBuffer_Create (NULL, &size, bitmap);
$ assert (dc); if (!dc) return NULL;
$ txSetDefaults (dc);
$ if (!_txCanvas_UserDCs) return dc;
$ txAutoLock _lock;
$ _txCanvas_UserDCs->push_back (dc);
$ if (_txCanvas_UserDCs->size() >= _TX_BUFSIZE)
{$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загрузили уже %d HDC, системе может стать плохо.", (int) _txCanvas_UserDCs->size()); }
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateDIBSection (double sizeX, double sizeY, RGBQUAD** pixels /*= NULL*/)
{
$1 RGBQUAD* buf = NULL;
$ if (!pixels) pixels = &buf;
$ BITMAPINFO info = {{ sizeof (info), ROUND (sizeX), ROUND (sizeY), 1, WORD (sizeof (RGBQUAD) * 8), BI_RGB }};
$ HDC dc = txCreateCompatibleDC (0, 0, Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (void**) pixels, NULL, 0));
$ RGBQUAD black = { 0, 0, 0, 255 };
$ for (int i = 0; i < sizeX * sizeY; i++) (*pixels)[i] = black;
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateDIBSection (double sizeX, double sizeY, COLORREF** pixels)
{
$1 return txCreateDIBSection (sizeX, sizeY, (RGBQUAD**) pixels);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txLoadImage (const char filename[], unsigned imageFlags /*= IMAGE_BITMAP*/, unsigned loadFlags /*= LR_LOADFROMFILE*/)
{
$1 if (_TX_ARGUMENT_FAILED (filename && *filename)) return NULL;
$ HBITMAP image = (HBITMAP) Win32::LoadImage ((loadFlags & LR_LOADFROMFILE)? NULL : GetModuleHandle (NULL),
filename, imageFlags, 0, 0, loadFlags);
$ if (!image) return NULL;
$ HDC dc = txCreateCompatibleDC (0, 0, image);
$ if (!(loadFlags & LR_LOADFROMFILE)) return dc;
$ static std::map <std::string, unsigned> loadTimes;
$ std::string file = filename;
$ unsigned time = GetTickCount();
$ if ((long) (time - loadTimes [file]) < _TX_TIMEOUT)
{$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загружаете \"%s\" слишком часто, программа будет тормозить.", filename); }
$ loadTimes [file] = time;
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDeleteDC (HDC* pdc)
{
$1 if (_TX_ARGUMENT_FAILED (pdc)) return false;
$ HDC dc = *pdc;
$ bool ok = _txBuffer_Delete (pdc);
$ if (!ok) return false;
$ if (!_txCanvas_UserDCs) return ok;
$ txAutoLock _lock;
$ std::vector <HDC> ::iterator it = std::find (_txCanvas_UserDCs->begin(), _txCanvas_UserDCs->end(), dc);
$ if (it != _txCanvas_UserDCs->end()) { std::swap (*it, _txCanvas_UserDCs->back()); _txCanvas_UserDCs->pop_back(); }
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDeleteDC (HDC dc)
{
$1 return txDeleteDC (&dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txBitBlt (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, unsigned operation /*= SRCCOPY*/)
{
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
$ return txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), operation)), destImage);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txBitBlt (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txBitBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource);
}
//-----------------------------------------------------------------------------------------------------------------
bool txTransparentBlt (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, COLORREF transColor /*= TX_BLACK*/)
{
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
#if !defined (NDEBUG)
$ if (!(0 <= xSource && xSource + width <= size.x &&
0 <= ySource && ySource + height <= size.y))
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
"функция txTransparentBlt() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y);
}
#endif
$ bool ok = (Win32::TransparentBlt != NULL);
$ if (ok)
{
$ ok &= txGDI (!!(Win32::TransparentBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), transColor)),
destImage);
}
else
{
$ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)),
destImage);
}
return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txTransparentBlt (double xDest, double yDest, HDC sourceImage,
COLORREF transColor /*= TX_BLACK*/, double xSource /*= 0*/, double ySource /*= 0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txTransparentBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, transColor);
}
//-----------------------------------------------------------------------------------------------------------------
bool txAlphaBlend (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/)
{
// Это проверки того, правильные ли HDC вы передали в функцию.
// Не бойтесь долларов - <s>это не запрещенная валюта</s> это макросы для отладки TXLib'а.
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
// Это автоматическое определение размеров картинки (точнее, HDC источника - source) с помощью txGetExtent().
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
// Это проверка того, что картинка (или ее часть) правильно попадает в окно (точнее, HDC приемника - destination, dest).
// Если она "вылезает" из окна в любую сторону, то Win32::AlphaBlend не будет работать. Эта проверка происходит только
// в режиме отладки (когда не задан макрос NDEBUG - No Debugging, без отладки).
#if !defined (NDEBUG)
$ if (!(0 <= xSource && xSource + width <= size.x &&
0 <= ySource && ySource + height <= size.y))
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
"функция txAlphaBlend() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y);
}
#endif
// Это на случай, если параметр alpha вылезает за диапазон [0..1].
$ if (alpha < 0) alpha = 0;
$ if (alpha > 1) alpha = 1;
// Об этом см. ниже.
$ BITMAP bmap = { 0, 0, 0, 0, 0, 24 };
$ bool ok = !!Win32::GetObject (txGDI ((Win32::GetCurrentObject (sourceImage, OBJ_BITMAP)), sourceImage), sizeof (bmap), &bmap);
//
// * Структура BLENDFUNCTION *
//
// Эта структура определяет, как цвета пикселей окна (точнее, source DC) и картинки смешиваются при рисовании с учетом
// прозрачности.
//
// Параметры смешивания помещаются в группу из переменных - так называемую структуру. Переменные, входящие в структуру,
// называются компонентами структуры.
//
// В этой структуре более всех важен ее третий компонент (см. ниже), задающий прозрачность картинки.
// Если он равен нулю - то картинка будет считаться полностью прозрачной и вызов Win32::AlphaBlend ничего не нарисует.
// Если он равен 255 - то картинка скопируется в окно полностью, без эффекта прозрачности.
//
// С помощью четвертого компонента структуры система рисования Windows (Win32 GDI) узнает, надо ли учитывать альфа-канал
// в копируемой картинке или его надо игнорировать. Если в картинке фактически есть правильно построенный альфа-канал
// (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу
// AC_SRC_ALPHA.
//
// Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0.
//
// В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения
// функции txAlphaBlend().
//
// Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать
// альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы
// породите невнятный паленый код и безнадежно испортите себе карму. :((
//
// На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо.
//
// ____1____ 2 ___________3___________ _______________________4________________________
// / \ | / \ / \.
$ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) };
//
// * Вызов стандартной функции Win32::AlphaBlend() *
//
// Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend()
// вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите
// "Windows AlphaBlend function" и почитайте про ее параметры.
//
// Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не
// совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что
// эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s>
// это сделано для упрощения вызова функции txAlphaBlend().
$ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<--
{ // // vvvvv vvvvvv // <<--
$ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<==
sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<==
destImage); // ^^^^^ ^^^^^^ // <<--
} // ||||| |||||| // <<--
// См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<--
else
{
$ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)),
destImage);
$ ok = false;
}
// В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою
// аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а
// на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто.
// Хотя это и сложнее.</s>
// Но помни про паленый копипаст и карму, см. выше. Я предупредил.
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage,
double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txUseAlpha (HDC image)
{
$1 if (_TX_HDC_FAILED (image)) return NULL;
$ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP);
$ if (!bitmap) return NULL;
$ DIBSECTION dib = {};
$ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted;
$ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight };
$ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }};
$ RGBQUAD* buf = NULL;
$ bool isDIB = (dib.dsBm.bmPlanes == 1 &&
dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 &&
dib.dsBmih.biCompression == DIB_RGB_COLORS &&
dib.dsBm.bmBits);
$ if (!isDIB)
{
$ buf = new (std::nothrow) RGBQUAD [size.x * size.y];
$ if (!buf) return NULL;
$ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted;
}
else
{
$ buf = (RGBQUAD*) dib.dsBm.bmBits;
}
$ for (int y = 0; y < size.y; y++)
for (int x = 0; x < size.x; x++)
{
RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer
color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0);
color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0);
color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0);
}
$ if (!isDIB)
{
$ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted;
$ delete[] buf;
}
$ return image;
}
//-----------------------------------------------------------------------------------------------------------------
bool txSaveImage (const char filename[], HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (filename)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT size = txGetExtent (dc);
$ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER),
szImg = (size.x * size.y) * sizeof (RGBQUAD);
$ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs };
$ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB };
$ bool ok = true;
$ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y];
$ ok &= (buf != NULL);
$ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y,
buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted;
$ FILE* f = NULL;
$ if (ok) fopen_s (&f, filename, "wb");
$ ok &= (f != NULL);
$ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1);
$ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1);
$ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1);
$ ok &= (f && fclose (f) == 0);
$ delete[] buf;
$ buf = NULL;
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline void txRedrawWindow()
{
$1 txSleep (0);
}
//-----------------------------------------------------------------------------------------------------------------
inline int txUpdateWindow (int update /*= true*/)
{
$1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update);
}
//-----------------------------------------------------------------------------------------------------------------
inline int txBegin()
{
$1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1);
$ return _txCanvas_RefreshLock;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txEnd()
{
$1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1);
$ return _txCanvas_RefreshLock;
}
//-----------------------------------------------------------------------------------------------------------------
double txSleep (double time)
{
$1 LARGE_INTEGER start = {};
$ QueryPerformanceCounter (&start) asserted;
$ LARGE_INTEGER freq = {};
$ QueryPerformanceFrequency (&freq) asserted;
$ int lock = _txCanvas_RefreshLock;
$ _txCanvas_RefreshLock = 0;
$ HWND wnd = txWindow();
if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
$ Sleep (ROUND ((time >= 0)? time : 0));
$ _txCanvas_RefreshLock = lock;
$ LARGE_INTEGER stop = {};
$ QueryPerformanceCounter (&stop) asserted;
$ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart;
}
//-----------------------------------------------------------------------------------------------------------------
bool txLock (bool wait /*= true*/)
{
$0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
$ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; }
else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); }
}
//-----------------------------------------------------------------------------------------------------------------
bool txUnlock()
{
$0 LeaveCriticalSection (&_txCanvas_LockBackBuf);
$ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
inline T txUnlock (T value)
{
$1 txUnlock();
$ return value;
}
//-----------------------------------------------------------------------------------------------------------------
inline POINT txMousePos()
{
$1 POINT pos = {};
$ GetCursorPos (&pos);
$ if (txWindow())
{$ ScreenToClient (txWindow(), &pos); }
$ return pos;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txMouseX()
{
return txMousePos() .x;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txMouseY()
{
return txMousePos() .y;
}
//-----------------------------------------------------------------------------------------------------------------
inline unsigned txMouseButtons()
{
$1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0
((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1
((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/)
{
unsigned oldAttr = txGetConsoleAttr();
SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color);
return oldAttr;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txGetConsoleAttr()
{
CONSOLE_SCREEN_BUFFER_INFO con = {};
GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
return con.wAttributes;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txSetConsoleCursorPos (double x, double y)
{
$1 POINT fontSz = txGetConsoleFontSize();
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left),
(short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) };
$ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted;
$ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
$ return prev;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleCursorPos()
{
$1 POINT fontSz = txGetConsoleFontSize();
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
$ return pos;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleExtent()
{
$1 CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ POINT size = { con.srWindow.Right - con.srWindow.Left + 1,
con.srWindow.Bottom - con.srWindow.Top + 1 };
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
bool txClearConsole()
{
$1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (out, &con) asserted;
$ COORD start = {con.srWindow.Left, con.srWindow.Top};
$ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) *
(con.srWindow.Bottom - con.srWindow.Top + 1);
$ DWORD written = 0;
$ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted;
$ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted;
$ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted;
$ return written == len;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleFontSize()
{
$1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}};
$ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font));
$ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y };
$ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC());
$ POINT sizeFont = { size.cx, size.cy };
$ return sizeFont;
}
//-----------------------------------------------------------------------------------------------------------------
bool txTextCursor (bool blink /*= true*/)
{
$1 bool old = _txConsole_IsBlinking;
$ _txConsole_IsBlinking = blink;
$ return old;
}
//-----------------------------------------------------------------------------------------------------------------
bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/)
{
$1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT;
$ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC;
$ if (!filename) mode = SND_PURGE;
$ return !!Win32::PlaySound (filename, NULL, mode);
}
//-----------------------------------------------------------------------------------------------------------------
int txSpeak (const char* text, ...)
{
$1 bool verbose = false; (void) verbose;
$ bool async = false; (void) async;
$ for (; text && *text; text++)
{
if (*text == '\a') {$ async = true; }
else if (*text == '\v') {$ verbose = true; }
else break;
}
$ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!";
$ va_list arg; va_start (arg, text);
if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); }
$ va_end (arg);
#ifdef TX_USE_SPEAK
if (text && verbose) {$ printf ("%s", textA); }
$ int time = GetTickCount();
$ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L"";
$ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW));
$ static ISpVoice* voice = NULL;
$ if (text && !voice)
{
$ HRESULT res = Win32::CoInitialize (NULL);
if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); }
}
$ if (text && voice)
{
$ Win32::_fpreset();
$ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL);
$ tx_fpreset();
}
$ if (!text && voice)
{
$ voice->Release();
$ voice = NULL;
$ Win32::CoUninitialize();
}
$ return (voice)? GetTickCount() - time : -1;
#else
$ if (text)
{
$ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK);
$ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA);
$ txSetConsoleAttr (oldAttr);
}
$ return -1;
#endif
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[],
double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/)
{
$1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1;
$ int time = GetTickCount();
$ static char processUID [64] = "";
if (!*processUID)
{
$ FILETIME startTime = {}, null = {};
$ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted;
$ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo",
(unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted;
}
$ if (!fileName)
{
$ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern
$ return 0;
}
$ static const char* vlcPath = _txPlayVideo_FindVLC();
$ if (!vlcPath || _access (vlcPath, 0) != 0)
{
$ static int once = false;
$ if (*fileName && !once++)
{$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org "
"и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n"
"--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n"
"P.S. См. мое описание в TXLib Help."); }
$ return INT_MIN;
}
$ bool async = false;
if (*fileName == '\a') {$ async = true; fileName++; }
$ RECT rect = {};
if (wnd) {$ GetClientRect (wnd, &rect); }
if (!width) {$ width = rect.right; }
if (!height) {$ height = rect.bottom; }
// Create a child window to hold the video stream
$ const char* errPos = "ВНЕЗАПНО";
$ volatile HWND child = NULL;
$ if (wnd && (wnd == txWindow()))
{
$ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1);
$ static int number = 1;
$ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x,
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass };
$ child = txCreateExtraWindow (createData);
$ if (!child)
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos));
$ return INT_MIN+3;
}
$ BringWindowToTop (child);
$ wnd = child;
}
// Build the command line
if (!zoom && !wnd) {$ zoom = 1; }
$ char sZoom [64] = "--autoscale";
if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; }
$ static char cmd [MAX_PATH*2 + 1024] = "";
$ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit"
" %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s"
" --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging"
" --ignore-config --reset-config --no-one-instance --play-and-exit"
" --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file"
" --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events",
vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted;
$ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n",
x, y, width, height, fileName, zoom, gain, wnd, cmd);
$ if (!*fileName)
{
if (child) {$ txDestroyWindow (child); }
$ return (intptr_t) cmd;
}
$ if (!strstr (fileName, "://") && _access (fileName, 0) != 0)
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos));
if (child) {$ txDestroyWindow (child); }
$ return INT_MIN+1;
}
// Run VLC, run
$ PROCESS_INFORMATION vlc = {};
$ STARTUPINFO start = { sizeof (start) };
$ DWORD ret = 0;
$ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) &&
vlc.hProcess && vlc.hThread)
{
$ if (child)
{
$ assert (wnd == child);
$ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess);
}
$ if (!async)
{
$ WaitForSingleObject (vlc.hProcess, INFINITE);
$ GetExitCodeProcess (vlc.hProcess, &ret) asserted;
}
$ if (!child)
{
$ CloseHandle (vlc.hProcess) asserted;
}
$ CloseHandle (vlc.hThread) asserted;
$ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret);
}
else
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos));
$ if (child)
{$ txDestroyWindow (child); }
$ return INT_MIN+4;
}
#undef PROCESS_UID_
}
//-----------------------------------------------------------------------------------------------------------------
inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/)
{
$1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd);
}
//-----------------------------------------------------------------------------------------------------------------
LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
{
const UINT_PTR checkTimer = 1;
switch (msg)
{
case WM_CREATE:
{
$1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted;
}
break;
case WM_DESTROY:
{
$1 KillTimer (wnd, checkTimer) asserted;
$ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
$ if (vlc)
{
$ Win32::TerminateProcess (vlc, 0);
$ CloseHandle (vlc) asserted;
$ SetWindowLongPtr (wnd, GWLP_USERDATA, 0);
}
}
break;
case WM_TIMER:
{
HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT)
{
$1 DestroyWindow (wnd) asserted;
}
}
break;
default:
break;
}
return DefWindowProc (wnd, msg, wpar, lpar);
}
//-----------------------------------------------------------------------------------------------------------------
const char* _txPlayVideo_FindVLC()
{
$1 static char vlcPath [MAX_PATH] = "";
$ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath)))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath)))
{
$ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX);
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ return NULL;
}
//-----------------------------------------------------------------------------------------------------------------
// +--<<< Это вряд ли имеет отношение к тому, что вы ищете :)
// V Полезно смотреть не только вверх, но и вниз
WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/)
{
$1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc;
$ return old;
}
//-----------------------------------------------------------------------------------------------------------------
// +--<<< А это, наконец, искомое определение этой функции.
// | Смотрите по сторонам! Нужная вам функция где-то рядом.
// |
// v
bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()
{
txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n"
"txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n"
"Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. "
"Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n"
"Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом "
"с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n"
"Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы "
"установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n",
"Не получилось", MB_ICONSTOP);
// The truth is out there... (C++files)
return false;
}
//-----------------------------------------------------------------------------------------------------------------
// Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you.
inline bool txDisableAutoPause()
{
_txExit = true;
return true;
}
// P.S. This library contains more undocumented functions. Search them via "Luke" keyword.
//-----------------------------------------------------------------------------------------------------------------
void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/)
{
$1 assert (!_txIsBadReadPtr (address));
const unsigned char* p = (const unsigned char*) address;
$ unsigned x = 0;
$ unsigned attr = txGetConsoleAttr();
$ txSetConsoleAttr (FOREGROUND_WHITE);
$ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : ""));
$ txSetConsoleAttr (FOREGROUND_YELLOW);
$ for (x = 0; x < 16; x++) printf ("%02X ", x);
$ for (x = 0; x < 16; x++) printf ("%X", x);
$ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0";
$ const char xlatCh[] = "·" "·" "·" "·" "·" "·";
$ const size_t szCtrl = sizeof (isCtrl) - 1;
$ int oldCP = GetConsoleOutputCP();
$ for (int y = 0; y < 16; y++, p += 16)
{
txSetConsoleAttr (FOREGROUND_YELLOW);
printf ("\n" "%*p ", (int) sizeof (address) * 2, p);
int color = FOREGROUND_LIGHTGREEN;
for (x = 0; x < 16; x++)
{
txSetConsoleAttr (color + x/4%2);
printf ("%02X ", p[x]);
}
for (x = 0; x < 16; x++)
{
txSetConsoleAttr (color + x/4%2);
char c = p[x];
const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl);
if (ctrl) c = xlatCh [ctrl - isCtrl];
if (ctrl) SetConsoleOutputCP (1251);
printf ("%c", c);
if (ctrl) SetConsoleOutputCP (oldCP);
}
}
$ txSetConsoleAttr (attr);
$ printf ("\n");
$ if (pause)
{
$ SetConsoleOutputCP (_TX_CODEPAGE);
$ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP);
$ (void)_getch();
$ SetConsoleOutputCP (oldCP);
}
}
//-----------------------------------------------------------------------------------------------------------------
void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/,
bool readSource /*= true*/)
{
$1 unsigned attr = txGetConsoleAttr();
$ txSetConsoleAttr (FOREGROUND_LIGHTCYAN);
$ fprintf (stderr, "\n" "--------------------------------------------------\n"
"Трассировка стека из \"%s\" at %s (%d):\n\n"
"%s\n\n"
"--------------------------------------------------\n\n",
func, file, line, _txCaptureStackBackTrace (1, readSource));
$ txSetConsoleAttr (attr);
}
//-----------------------------------------------------------------------------------------------------------------
char* txDemangle (const char* mangledName, std::nomeow_t)
{
$1 if (!mangledName) return NULL;
$ char* typeName = NULL;
#if defined (_GCC_VER)
$ int err = 1;
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err;
if (typeName) {$ return typeName; }
#endif
$ unsigned short flags = 0;
$ if (mangledName[0] == '.')
{
$ mangledName++;
$ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY
}
$ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags));
if (typeName) {$ return typeName; }
$ return _strdup (mangledName);
}
//-----------------------------------------------------------------------------------------------------------------
std::string txDemangle (const char* mangledName)
{
$1 char* typeName = txDemangle (mangledName, std::nomeow);
$ std::string name (typeName? typeName : "");
$ free (typeName);
$ return name;
}
//-----------------------------------------------------------------------------------------------------------------
double txQueryPerformance()
{
$1 int maxTime = 500;
$ int maxSamples = 100;
$ POINT size = {100, 100};
$ HDC dc = _txBuffer_Create (txWindow(), &size, NULL);
$ assert (dc); if (!dc) return -1;
$ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1);
$ assert (mask);
$ LARGE_INTEGER freq = {};
$ QueryPerformanceFrequency (&freq) asserted;
$ LARGE_INTEGER start = {};
$ QueryPerformanceCounter (&start) asserted;
$ int samples = 0;
$ while (samples++ < maxSamples)
{
$ LARGE_INTEGER cur = {};
$ QueryPerformanceCounter (&cur) asserted;
$ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart;
$ if (t > maxTime) break;
// Draw test scene
$ for (int y = 0; y < size.y; y++)
for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc);
$ for (int y = 0; y < size.y; y += 10)
for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc);
$ txEllipse (0, 0, size.x, size.y, dc);
$ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc);
$ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted;
$ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted;
$ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted;
$ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted;
}
$ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask);
$ assert (mask);
$ _txBuffer_Delete (&dc);
$ return 3.0 * samples / sqrt (1.0 * size.x * size.y);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_TX_CPP11)
template <int txFramesToAverage>
#endif
double txGetFPS (int minFrames)
{
$1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0);
$ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time);
$ if (time.QuadPart - time0.QuadPart == 0)
{$ return 0; }
$ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq);
$ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart);
$ time0 = time;
$ if (txFramesToAverage == 0) return fps;
$ static double average [txFramesToAverage] = {};
$ static unsigned n = 0;
$ average [n++ % txFramesToAverage] = fps;
$ unsigned nn = MIN (n, (unsigned) sizearr (average));
$ static double median [txFramesToAverage] = {};
$ std::copy (average, average + nn, median);
$ std::nth_element (median, median + nn/2, median + nn);
$ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0;
$ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txExtractColor (COLORREF color, COLORREF component)
{
$1 switch (component)
{
case TX_RED:
case TX_HUE: $ return (color >> 0) & 0xFF;
case TX_GREEN:
case TX_SATURATION: $ return (color >> 8) & 0xFF;
case TX_BLUE:
case TX_LIGHTNESS: $ return (color >> 16) & 0xFF;
default: $ return CLR_INVALID;
}
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txRGB2HSL (COLORREF rgbColor)
{
$1 int r = (int) txExtractColor (rgbColor, TX_RED),
g = (int) txExtractColor (rgbColor, TX_GREEN),
b = (int) txExtractColor (rgbColor, TX_BLUE);
$ double m1 = MAX (MAX (r, g), b) / 255.0,
m2 = MIN (MIN (r, g), b) / 255.0,
dm = m1 - m2,
sm = m1 + m2,
ir = r / 255.0,
ig = g / 255.0,
ib = b / 255.0,
ih = 0,
is = 0,
il = sm / 2;
$ const double prec = 0.001;
$ if (fabs (dm) < prec)
{
$ is = dm / ((sm <= 1)? sm : (2-sm));
$ double cr = (m1 - ir) / dm,
cg = (m1 - ig) / dm,
cb = (m1 - ib) / dm;
$ if (fabs (ir - m1) < prec) ih = cb - cg;
$ if (fabs (ig - m1) < prec) ih = 2 + cr - cb;
$ if (fabs (ib - m1) < prec) ih = 4 + cg - cr;
}
$ ih = (ih >= 0)? ih*60 : ih*60 + 360;
$ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255));
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txHSL2RGB (COLORREF hslColor)
{
$1 struct xRGB
{
static double calc (double h, double m1, double m2)
{
$ if (h < 0) h += 360;
$ if (h > 360) h -= 360;
$ return (h < 60)? m1 + (m2-m1) * h / 60 :
(h < 180)? m2 :
(h < 240)? m1 + (m2-m1) * (240-h) / 60 :
m1;
}
};
$ int h = (int) txExtractColor (hslColor, TX_HUE),
s = (int) txExtractColor (hslColor, TX_SATURATION),
l = (int) txExtractColor (hslColor, TX_LIGHTNESS);
$ double ih = h / 255.0 * 360.0,
il = l / 100.0,
is = s / 100.0,
m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is,
m1 = 2 * il - m2,
ir = s? xRGB::calc (ih + 120, m1, m2) : il,
ig = s? xRGB::calc (ih, m1, m2) : il,
ib = s? xRGB::calc (ih - 120, m1, m2) : il;
$ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255));
}
//-----------------------------------------------------------------------------------------------------------------
inline double random (std::nomeow_t, double left, double right)
{
return left + (right - left) * ((double) rand() / RAND_MAX);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename Tx, typename Ta, typename Tb>
inline bool In (std::nomeow_t, Tx x, Ta a, Tb b)
{
return a <= x && x <= b;
}
//-----------------------------------------------------------------------------------------------------------------
inline int random (int range)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return rand() % range;
}
//-----------------------------------------------------------------------------------------------------------------
inline double random (double left, double right)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return random (std::nomeow, left, right);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename Tx, typename Ta, typename Tb>
inline bool In (Tx x, Ta a, Tb b)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return In (std::nomeow, x, a, b);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool In (const POINT& pt, const RECT& rect)
{
if (_TX_ARGUMENT_FAILED (&pt)) return false;
if (_TX_ARGUMENT_FAILED (&rect)) return false;
return In (std::nomeow, pt.x, rect.left, rect.right) &&
In (std::nomeow, pt.y, rect.top, rect.bottom);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool In (const COORD& pt, const SMALL_RECT& rect)
{
if (_TX_ARGUMENT_FAILED (&pt)) return false;
if (_TX_ARGUMENT_FAILED (&rect)) return false;
return In (std::nomeow, pt.X, rect.Left, rect.Right) &&
In (std::nomeow, pt.Y, rect.Top, rect.Bottom);
}
//-----------------------------------------------------------------------------------------------------------------
void tx_fpreset()
{
$1 txAutoLock _lock;
$ Win32::_fpreset();
$ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW
#if !defined (__CYGWIN__)
$ unsigned old87 = 0;
$ if (_controlfp_s (&old87, 0, 0) == 0)
{$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM
#else
$ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM
#endif
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
inline T zero() { T __zero = {}; return __zero; }
//}
//=================================================================================================================
//=================================================================================================================
//{ txPrintf() implementation
// Реализация txPrintf()
//=================================================================================================================
#if defined (_TX_CPP11)
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args);
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt);
template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg);
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg);
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt);
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args)
{
$1 assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%') {$}
else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); }
else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); }
else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); }
else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt)
{
$1 assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (!fmt[0]) {$}
else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); }
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt)
{
$1 assert (&stream);
$ assert (fmt);
$ while (*fmt)
{
if (fmt[0] == '%')
{
if (fmt[1] == '%') fmt++;
else break;
}
stream << *fmt++;
}
$ }
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg)
{
$1 assert (&stream);
$ assert (fmt);
$ if (_TX_ARGUMENT_FAILED (&arg)) return;
if (fmt[0] == '%') {$}
else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); }
$ fmt++;
$ char oldFill = stream.fill (' ');
$ std::ios_base::fmtflags oldFlags = stream.flags();
$ for (;;) switch (*fmt)
{
case '-': $ stream << std::left; fmt++; break;
case '+': $ stream << std::showpos; fmt++; break;
case ' ': $ stream.fill (' '); fmt++; break;
case '#': $ stream << std::showbase; fmt++; break;
case '0': $ stream.fill ('0'); fmt++; break;
default: $ goto end;
}
end:
$ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0);
$ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0;
if (width) {$ stream << std::setw (width); }
if (prec) {$ stream << std::setprecision (prec); }
$ fmt += strspn (fmt, "hljztL");
$ switch (*fmt)
{
case '$':
case '?': $ break;
case 'd':
case 'i':
case 'u': $ stream << std::dec; break;
case 'o': $ stream << std::oct; break;
case 'x': $ stream << std::hex; break;
case 'X': $ stream << std::hex << std::uppercase; break;
case 'f': $ stream << std::fixed; break;
case 'F': $ stream << std::fixed << std::uppercase; break;
case 'e': $ stream << std::scientific; break;
case 'E': $ stream << std::scientific << std::uppercase; break;
case 'g': $ break;
case 'G': $ stream << std::uppercase; break;
case 'a': $ break;
case 'A': $ stream << std::uppercase; break;
case 'c':
case 's':
case 'p': $ break;
default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break;
}
$ fmt++;
if (&arg) {$ stream << arg; }
else {$ stream << "(null)"; }
$ stream.fill (oldFill);
$ stream.flags (oldFlags);
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg)
{
$1 assert (fmt);
if (_TX_ARGUMENT_FAILED (arg)) return;
if (fmt[0] == '%' && fmt[1] == 'n') {$}
else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); }
$ *arg = (int) stream.str().length();
$ fmt += 2;
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; }
inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); }
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&stream)) return 0;
$ if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ const char* fmt = format;
$ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...);
$ return (int) stream.str().length();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0;
$ if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ if (size > 0) size--;
$ buffer[size] = 0;
$ if (!size) return 0;
$ std::ostringstream stream;
$ stream.rdbuf() -> pubsetbuf (buffer, size);
$ txPrintf (stream, format, args...);
$ return (int) stream.str().length();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline std::string txFormat (const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&format)) return "";
$ std::ostringstream stream;
$ txPrintf (stream, format, args...);
$ return stream.str();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ return printf ("%s", txFormat (format, args...) .c_str());
}
#endif
//-----------------------------------------------------------------------------------------------------------------
int _txPrintfCheck (const char* format, ...) tx_printfy (1);
inline int _txPrintfCheck (const char*, ...) { return 0; }
//}
//=================================================================================================================
//=================================================================================================================
//{ txDialog methods implementation
// Реализация методов класса txDialog
//
// See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx
// [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743
// [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753
//=================================================================================================================
txDialog::txDialog () :
layout_ (NULL)
{$1}
//-----------------------------------------------------------------------------------------------------------------
txDialog::txDialog (const Layout* layout) :
layout_ (layout)
{$1}
//-----------------------------------------------------------------------------------------------------------------
const txDialog::Layout* txDialog::setLayout (const Layout* layout)
{
$1 assert (layout);
$ return ::std::swap (layout_, layout), layout;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txDialog::dialogBox (WORD resourceID)
{
$1 const char* resName = (char*)(uintptr_t) resourceID;
$ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0;
$ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/)
{
$1 if (!layout) layout = layout_;
$ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0;
$ if (!bufsize) bufsize = 1024;
$ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize);
$ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0;
$ const Layout* dlg = &layout[0];
$ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 };
$ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize,
(dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0,
dlg->x, dlg->y, dlg->sx, dlg->sy,
dlg->caption? dlg->caption : def.caption,
dlg->font? dlg->font : def.font,
dlg->fontsize? dlg->fontsize : def.fontsize, NULL);
$ WORD i = 0;
$ for (i = 1; layout[i].wndclass != END; ++i)
{
$ const Layout* item = &layout[i];
$ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl),
item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy,
item->id, (const char*)(uintptr_t) item->wndclass, item->caption);
}
$ tmpl->cdit = (unsigned short) (i-1);
$ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
$ GlobalFree (tmpl);
$ return res;
}
//-----------------------------------------------------------------------------------------------------------------
int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM)
{
$1 switch (msg)
{
case WM_INITDIALOG: $ SetForegroundWindow (wnd);
$ break;
case WM_COMMAND: $ switch (LOWORD (wParam))
{
case IDOK:
case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow());
$ EndDialog (wnd, (uintptr_t) this);
$ break;
default: $ break;
}
$ break;
default: $ break;
}
$ return FALSE;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
$1 static txDialog* this__ = NULL;
$ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam;
$ if (!this__) return FALSE;
$ return this__-> dialogProc (wnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle,
WORD controls, short x, short y, short cx, short cy,
const char caption[], const char font[], WORD fontsize, const char menu[])
{
$1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL;
$ WORD* pw = (WORD*) globalMem;
$ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->cdit = controls;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ if (menu > (const char*) 0xFFFF)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
else
{
$ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0);
$ *pw++ = (WORD)(uintptr_t) menu;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ if (style & DS_SETFONT)
{
$ *pw++ = fontsize;
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ return pw;
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle,
short x, short y, short cx, short cy,
WORD id, const char wclass[], const char caption[])
{
$1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL;
$ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary
$ ((ULONG&) pw) += 3;
$ ((ULONG&) pw) >>= 2;
$ ((ULONG&) pw) <<= 2;
$ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ tmpl->id = id;
$ if (HIWORD (wclass) == 0xFFFF)
{
$ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass));
$ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass));
}
else if (wclass)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ *pw++ = 0;
$ return pw;
}
//}
//=================================================================================================================
//=================================================================================================================
//{ Cleaning up the utility macros
// Очистка служебных макросов
//=================================================================================================================
#undef $
#undef $0
#undef $1
#undef $2
#undef $3
#undef $4
#undef $5
#undef $6
#undef $7
#undef $8
#undef $9
#undef $$
//}
//=================================================================================================================
//! @endcond
//=================================================================================================================
//{ Experimental Debugging macros
//! @name Экспериментальные отладочные макросы
//=================================================================================================================
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Misc
//! @brief Отладочная печать переменной во время вычисления выражения или участка кода
//! во время его выполнения.
//!
//! Сделай приятными твои <i>круглые сутки), MB_ICONSTOP | MB_SYSTEMMODAL | MB_YESNOCANCEL);
11744 SetLastError (winErr);
11748 #if !defined (__CYGWIN__)
11749 _doserrno = dosErr;
11752 if (((options & isFatal) && !IsDebuggerPresent()) || ret == IDYES)
11756 Win32::TerminateProcess (GetCurrentProcess(), EXIT_FAILURE);
11766 #if defined (_MSC_VER)
11768 int _txOnErrorReport (
int type,
const char* text,
int* ret)
11785 const char startReport[] =
"Detected memory leaks!\n",
11786 endReport[] =
"Object dump complete.\n";
11788 if (strcmp (text, startReport) == 0)
11790 _txOnErrorReport (type,
"\n", NULL);
11791 _txOnErrorReport (type,
_TX_VERSION " - ERROR: ", NULL);
11792 _txOnErrorReport (type,
"Внимание: Обнаружены утечки памяти! (Для поиска используйте _TX_ALLOC_BREAK.)\n", NULL);
11793 _txOnErrorReport (type,
"\n", NULL);
11796 size_t len = strlen (text);
11802 HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
11803 WriteFile (err, text, (DWORD) strlen (text), &n, NULL);
11809 HANDLE log = CreateFile (
_txLogName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
11810 if (log == INVALID_HANDLE_VALUE)
break;
11812 SetFilePointer (log, 0, NULL, FILE_END);
11813 WriteFile (log, text, (DWORD) strlen (text), &n, NULL);
11822 return (type == _CRT_WARN);
11829 int txMessageBox (
const char text[],
const char header[],
unsigned flags )
11831 $5
static wchar_t textW [
_TX_BIGBUFSIZE *
sizeof (wchar_t)] = L
"[NULL text]";
11832 $
static wchar_t headerW [
_TX_BUFSIZE *
sizeof (wchar_t)] = L
"[NULL header]";
11834 if (text) {$ MultiByteToWideChar (
_TX_CODEPAGE, 0, text, -1, textW,
sizearr (textW)) || memset (textW, 0,
sizeof (textW)); }
11835 if (header) {$ MultiByteToWideChar (
_TX_CODEPAGE, 0, header, -1, headerW,
sizearr (headerW)) || memset (headerW, 0,
sizeof (headerW)); }
11837 $ HWND wnd = _txCanvas_Window;
11838 $
int ret = MessageBoxW ((wnd && IsWindowVisible (wnd))? wnd : _TX_CALL (Win32::GetConsoleWindow,()),
11839 textW, headerW, flags | MB_SETFOREGROUND | MB_TOPMOST | MB_OKCANCEL );
11840 $
if (ret == IDCANCEL)
11842 $ SendNotifyMessage (
txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0);
11853 $1 HWND wnd = GetForegroundWindow();
11855 return (GetAsyncKeyState (key) & 0x8000) &&
11856 (wnd ==
txWindow() || wnd == Win32::GetConsoleWindow());
11861 bool txNotifyIcon (
unsigned flags,
const char title[],
const char format[], ...)
11863 $5
if (_TX_ARGUMENT_FAILED (format))
return false;
11865 $ va_list arg; va_start (arg, format);
11868 #if defined (_WIN32_IE) && (_WIN32_IE >= 0x0500)
11870 $ NOTIFYICONDATA nid = {
sizeof (nid) };
11872 $ nid.uFlags = NIF_ICON | NIF_TIP | NIF_INFO;
11875 $ nid.hIcon = _txCreateTXIcon (16);
assert (nid.hIcon);
11876 $
strncpy_s (nid.szTip, sizeof (nid.szTip),
"TXLib Information", sizeof (nid.szTip));
11877 $
strncpy_s (nid.szInfoTitle, sizeof (nid.szInfoTitle), (title? title :
"TXLib сообщает"), sizeof (nid.szInfoTitle) - 1);
11878 $ _tx_vsnprintf_s (nid.szInfo, sizeof (nid.szInfo), format, arg);
11879 $ nid.dwInfoFlags = flags;
11883 $ ok &= !!Shell_NotifyIcon (NIM_ADD, (::NOTIFYICONDATA*) &nid);
11884 $ ok &= !!Shell_NotifyIcon (NIM_MODIFY, (::NOTIFYICONDATA*) &nid);
11886 $
if (nid.hIcon) DestroyIcon (nid.hIcon)
asserted;
11891 $ _tx_vsnprintf_s (nid_szInfo,
sizeof (nid_szInfo), format, arg);
11895 $ (void)flags; (void)title;
11905 void _txTrace (
const char file[],
int line,
const char func[],
const char msg[] , ...)
11907 unsigned id = GetCurrentThreadId();
11909 const char marks[2][2][3] = {{
"uU",
"cC"}, {
"mM",
"??"}};
11911 char mark = marks [
id == _txMainThreadId] [
id == _txCanvas_ThreadId] [(_txLoc::Cur.inTX > 0)];
11916 va_list arg; va_start (arg, msg);
11917 _tx_vsnprintf_s (msgStr,
sizeof (msgStr) - 1, msg, arg);
11925 "cC"[_txConsole],
"mM"[_txMain],
"dD"[_txIsDll],
"rR"[_txRunning],
"eE"[_txExit],
11926 _txLoc::Cur.trace, mark,
11928 (
int)
sizeof (__FILE__) - 1, (file? file :
"(NULL file)"), line,
11929 2 * (_txLoc::Cur.inTX - 1) * !!func,
"", (func? func :
""),
11931 ((*msgStr && func)?
": " :
""), msgStr);
11938 if (!format)
return 0;
11940 enum { msgbox = 1, print = 2, compr = 4 };
11943 for (; format && *format; format++)
11945 if (*format ==
'\a') options |= msgbox;
11946 else if (*format ==
'\f') options |= print;
11947 else if (*format ==
'\r') options |= compr;
11953 va_list arg; va_start (arg, format);
11954 int n = (int) _tx_vsnprintf_s (text,
sizeof (text) - 1-1, format, arg);
11957 struct __ {
static int trimSpaces (
char str[])
11959 char *dst = str, *src = str;
11961 for (
char d =
' '; d; src++)
11962 if (isspace ((
unsigned char)(*src))) {
if (d !=
' ') *dst++ = d =
' '; }
11963 else *dst++ = d = *src;
11965 return (
int) (dst - str - 1);
11968 if (options & compr) n = __::trimSpaces (text);
11970 OutputDebugString (text);
11972 if (options & print) fprintf (stderr,
"%s", text);
11974 if (options & msgbox)
txMessageBox (text,
"Оказывается, что , MB_ICONEXCLAMATION);
return n;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _tx_snprintf_s (char stream[], intptr_t size, const char format[], ...)
{
if (!format) return 0;
va_list arg; va_start (arg, format);
intptr_t ret = _tx_vsnprintf_s (stream, size, format, arg);
va_end (arg);
return ret;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t _tx_vsnprintf_s (char stream[], intptr_t size, const char format[], va_list arg)
{
if (!stream || !format) return 0;
#if defined (_TRUNCATE)
intptr_t ret = _vsnprintf_s (stream, size, _TRUNCATE, format, arg);
#else
intptr_t ret = _vsnprintf (stream, size, format, arg);
#endif
if (ret < 0 && size >= 4)
{
const char ellipsis[] = "...";
size_t szEllipsis = sizeof (ellipsis) - 1;
strncpy_s (stream + size - szEllipsis, szEllipsis+1, ellipsis, szEllipsis);
}
return (ret >= 0)? ret : size;
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (__CYGWIN__)
int _getch()
{
termios oldattr = {}; tcgetattr (STDIN_FILENO, &oldattr);
termios newattr = oldattr;
newattr.c_lflag &= ~(ICANON | ECHO);
tcsetattr (STDIN_FILENO, TCSANOW, &newattr);
int ch = getchar();
tcsetattr (STDIN_FILENO, TCSANOW, &oldattr);
return ch;
}
//-----------------------------------------------------------------------------------------------------------------
int _putch (int ch)
{
termios old = {}; tcgetattr (STDOUT_FILENO, &old);
termios cur = old;
cur.c_lflag &= ~ICANON;
cur.c_lflag |= ECHO;
tcsetattr (STDOUT_FILENO, TCSANOW, &cur);
putchar (ch);
tcsetattr (STDOUT_FILENO, TCSANOW, &old);
return ch;
}
//-----------------------------------------------------------------------------------------------------------------
int _kbhit()
{
termios old = {}; tcgetattr (STDIN_FILENO, &old);
termios cur = old;
cur.c_lflag &= ~(ICANON | ECHO);
cur.c_cc[VMIN] = 1;
cur.c_cc[VTIME] = 0;
tcsetattr (STDIN_FILENO, TCSAFLUSH, &cur);
fd_set fd = {}; FD_SET (STDIN_FILENO, &fd);
timeval tv = {};
int res = select (STDIN_FILENO + 1, &fd, NULL, NULL, &tv) && FD_ISSET (STDIN_FILENO, &fd);
tcsetattr (STDIN_FILENO, TCSAFLUSH, &old);
return res;
}
#endif
//}
//-----------------------------------------------------------------------------------------------------------------
//-----------------------------------------------------------------------------------------------------------------
//{ Information
//-----------------------------------------------------------------------------------------------------------------
const char* txGetModuleFileName (bool fileNameOnly /*= true*/)
{
static char name[MAX_PATH] = "";
if (!*name)
{
if (!GetModuleFileName (NULL, name, sizeof (name) - 1)) *name = 0;
char* ext = strrchr (name, '.');
if (ext) _strlwr_s (ext, sizeof (name) - 1 - (ext - name));
}
assert (*name);
if (fileNameOnly) return name;
static char fullName[MAX_PATH] = "";
strncpy_s (fullName, sizeof (fullName), name, sizeof (fullName) - 1);
char* title = strrchr (fullName, '\\'); if (!title) title = fullName;
char* ext = strrchr (fullName, '.'); if (!ext) ext = fullName + strlen (fullName);
size_t sz = sizeof (fullName) - (ext - fullName);
strncpy_s (ext, sz-1, " - TXLib", sz);
return title + 1;
}
//-----------------------------------------------------------------------------------------------------------------
const char* _txAppInfo()
{
$1 time_t timeT = time (NULL) - clock()/CLOCKS_PER_SEC;
char timeS[32] = "";
ctime_s (timeS, sizeof (timeS), &timeT);
static char text[_TX_BUFSIZE] = "";
char cwd [MAX_PATH] = "";
_tx_snprintf_s (text, sizeof (text) - 1,
"Developed with:\n\n"
"The Dumb Artist Library (TX Library)\n"
_TX_VERSION "\n" _TX_AUTHOR "\n"
"See license on: http://txlib.ru\n\n"
"TXLib file:" "\t" __FILE__ "\n"
"Compiled:" "\t" __DATE__ " " __TIME__ ", " __TX_COMPILER__ ", %s, %d-bit, " _TX_BUILDMODE "\n"
"Started:" "\t" "%.6s %.4s %.8s\n\n"
"Run file:" "\t" "%s\n"
"Directory:" "\t" "%s",
#if defined (_MSC_VER)
"MSVC Runtime",
#elif defined (__CYGWIN__)
"Cygwin Runtime",
#elif defined (_GCC_VER) && defined (_WIN64)
__mingw_get_crt_info(),
#else
"MinGW Runtime " TX_QUOTE (__MINGW32_MAJOR_VERSION) "." TX_QUOTE (__MINGW32_MINOR_VERSION),
#endif
(sizeof (void*) == sizeof (DWORD))? 32 : 64,
timeS + 4, timeS + 20, timeS + 11, // These offsets are ANSI standardized
txGetModuleFileName(),
_getcwd (cwd, sizeof (cwd) - 1));
return text;
}
//}
//-----------------------------------------------------------------------------------------------------------------
//! @}
//}
//=================================================================================================================
//=================================================================================================================
//{ TXLib API implementation
// Реализация TXLib API
//=================================================================================================================
inline const char* txVersion()
{
return _TX_VERSION;
}
//-----------------------------------------------------------------------------------------------------------------
inline unsigned txVersionNumber()
{
return _TX_VER;
}
//-----------------------------------------------------------------------------------------------------------------
inline HWND txWindow()
{
$0 return _txCanvas_Window;
}
//-----------------------------------------------------------------------------------------------------------------
inline HDC& txDC()
{
$0 return _txCanvas_BackBuf[0];
}
//-----------------------------------------------------------------------------------------------------------------
inline RGBQUAD* txVideoMemory()
{
return _txCanvas_Pixels;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetExtent (HDC dc /*= txDC()*/)
{
$0 static POINT err = {-1, -1};
if (!dc) {$ POINT screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) }; return screen; };
if (_TX_DEFAULT_HDC_FAILED (dc)) {$ return err; }
$ BITMAP bmap = {};
$ txGDI (Win32::GetObject (Win32::GetCurrentObject (dc, OBJ_BITMAP), sizeof (bmap), &bmap), dc) asserted;
$ POINT size = { bmap.bmWidth, bmap.bmHeight };
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetExtentX (HDC dc /*= txDC()*/)
{
return txGetExtent (dc) .x;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetExtentY (HDC dc /*= txDC()*/)
{
return txGetExtent (dc) .y;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDestroyWindow (HWND wnd /*= txWindow()*/)
{
$1 if (!wnd || !txWindow()) return false;
$ if (wnd != txWindow())
{
$ return !!SendNotifyMessage (txWindow(), _TX_WM_DESTROYWND, 0, (LPARAM) wnd);
}
$ if (SendNotifyMessage (txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0) == 0) return false;
$ if (_txMain)
{
$ txNotifyIcon (NIIF_WARNING, NULL, "\n" "Очень, очень плохо завершать программу через txDestroyWindow().\n\n"
"Возвращайтесь через main(), там вам будут рады.\n");
$ Sleep (_TX_TIMEOUT);
}
$ _txWaitFor (!_txCanvas_Window, _TX_TIMEOUT);
$ return _txCanvas_Window == NULL;
}
//-----------------------------------------------------------------------------------------------------------------
HPEN txSetColor (COLORREF color, double thickness /*= 1*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HPEN pen = Win32::CreatePen ((color == TX_TRANSPARENT? PS_NULL : PS_SOLID), ROUND (thickness), color);
$ if (!pen) return (HPEN) NULL;
$ if (!_txBuffer_Select (pen, dc))
{
$ Win32::DeleteObject (pen);
$ return (HPEN) NULL;
}
$ if (txGDI (Win32::SetTextColor (dc, color), dc) == CLR_INVALID)
{$ return (HPEN) NULL; }
$ return pen;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txColor (double red, double green, double blue)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255));
$ return txSetColor (color)? color : CLR_INVALID;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txGetColor (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_PEN)), dc);
$ assert (obj); if (!obj) return CLR_INVALID;
$ union { EXTLOGPEN extLogPen; LOGPEN LogPen; } buf = {};
$ int size = Win32::GetObject (obj, 0, NULL);
$ Win32::GetObject (obj, sizeof (buf), &buf) asserted;
$ return (size == sizeof (LOGPEN))? buf.LogPen.lopnColor : buf.extLogPen.elpColor;
}
//-----------------------------------------------------------------------------------------------------------------
HBRUSH txSetFillColor (COLORREF color, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HBRUSH brush = (color == TX_TRANSPARENT)? (HBRUSH) Win32::GetStockObject (HOLLOW_BRUSH) : Win32::CreateSolidBrush (color);
$ return (_txBuffer_Select (brush, dc))? brush : (Win32::DeleteObject (brush), (HBRUSH) NULL);
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txFillColor (double red, double green, double blue)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ COLORREF color = RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255));
$ return txSetFillColor (color)? color : CLR_INVALID;
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txGetFillColor (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ HGDIOBJ obj = txGDI ((Win32::GetCurrentObject (dc, OBJ_BRUSH)), dc);
$ assert (obj); if (!obj) return CLR_INVALID;
$ LOGBRUSH buf = {};
$ txGDI ((Win32::GetObject (obj, sizeof (buf), &buf)), dc) asserted;
$ return buf.lbColor;
}
//-----------------------------------------------------------------------------------------------------------------
bool txClear (HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT size = txGetExtent (dc);
$ return txGDI (!!(Win32::PatBlt (dc, 0, 0, size.x, size.y, PATCOPY)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txSetPixel (double x, double y, COLORREF color, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ txGDI ((Win32::SetPixel (dc, ROUND (x), ROUND (y), color)), dc);
$ return true;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txPixel (double x, double y, double red, double green, double blue, HDC dc /*= txDC()*/)
{
$1 if (red > 1) red = 1; if (red < 0) red = 0;
$ if (green > 1) green = 1; if (green < 0) green = 0;
$ if (blue > 1) blue = 1; if (blue < 0) blue = 0;
$ return txSetPixel (x, y, RGB (ROUND (red * 255), ROUND (green * 255), ROUND (blue * 255)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
inline COLORREF txGetPixel (double x, double y, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return CLR_INVALID;
$ return txGDI ((Win32::GetPixel (dc, ROUND (x), ROUND (y))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txLine (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ bool ok = txGDI ((Win32::MoveToEx (dc, ROUND (x0), ROUND (y0), NULL)), dc);
$ ok &= txGDI ((Win32::LineTo (dc, ROUND (x1), ROUND (y1) )), dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txRectangle (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::Rectangle (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txPolygon (const POINT points[], int numPoints, HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (points)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI (!!(Win32::Polygon (dc, points, numPoints)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txEllipse (double x0, double y0, double x1, double y1, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::Ellipse (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txCircle (double x, double y, double r)
{
$1 return txEllipse (x-r, y-r, x+r, y+r);
}
//-----------------------------------------------------------------------------------------------------------------
bool txArc (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Arc (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txPie (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Pie (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txChord (double x0, double y0, double x1, double y1, double startAngle, double totalAngle, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT center = { ROUND ((x0 + x1) /2), ROUND ((y0 + y1) /2) };
$ double start = startAngle * txPI/180,
end = (startAngle + totalAngle) * txPI/180;
$ return txGDI (!!(Win32::Chord (dc, ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1),
ROUND (center.x + 1E3*cos (start)), ROUND (center.y - 1E3*sin (start)),
ROUND (center.x + 1E3*cos (end)), ROUND (center.y - 1E3*sin (end)))), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txFloodFill (double x, double y,
COLORREF color /*= TX_TRANSPARENT*/, DWORD mode /*= FLOODFILLSURFACE*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ if (color == TX_TRANSPARENT) color = txGetPixel (x, y, dc);
$ return txGDI (!!(Win32::ExtFloodFill (dc, ROUND (x), ROUND (y), color, mode)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txTextOut (double x, double y, const char text[], HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (text)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ int len = (int) strlen (text);
$ bool ok = txGDI (!!(Win32::TextOut (dc, ROUND (x), ROUND (y), text, len)), dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDrawText (double x0, double y0, double x1, double y1, const char text[],
unsigned format /*= DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_WORD_ELLIPSIS*/,
HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (text)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
#if !defined (NDEBUG)
$ if (x0 > x1)
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Параметр x0 = %lg больше, чем x1 = %lg. Текст выводиться не будет.", x0, x1);
}
$ if (y0 > y1)
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Параметр y0 = %lg больше, чем y1 = %lg. Текст выводиться не будет.", y0, y1);
}
#endif
$ RECT r = { ROUND (x0), ROUND (y0), ROUND (x1), ROUND (y1) };
$ if (!strchr (text, '\n')) format |= DT_SINGLELINE;
$ unsigned prev = txSetTextAlign (TA_LEFT | TA_TOP | TA_NOUPDATECP, dc);
$ bool ok = false;
$ if (Win32::DrawText)
{
$ ok = !!txGDI ((Win32::DrawText (dc, text, -1, &r, format)), dc);
$ Win32::GetPixel (dc, 0, 0);
$ ok = true;
}
else
{
$ txTextOut ((x0 + x1) / 2, (y0 + y1) / 2, text);
$ ok = false;
}
$ txSetTextAlign (prev, dc);
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
HFONT txSelectFont (const char name[], double sizeY, double sizeX /*= -1*/,
int bold /*= FW_DONTCARE*/, bool italic /*= false*/, bool underline /*= false*/,
bool strikeout /*= false*/, double angle /*= 0*/,
HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (name)) return NULL;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return NULL;
$ HFONT font = txFontExist (name)?
Win32::CreateFont (ROUND (sizeY), ROUND ((sizeX >= 0)? sizeX : sizeY/3),
ROUND (angle*10), 0, bold, italic, underline, strikeout,
RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, name)
:
(HFONT) Win32::GetStockObject (SYSTEM_FIXED_FONT);
$ _txBuffer_Select (font, dc);
$ return font;
}
//-----------------------------------------------------------------------------------------------------------------
SIZE txGetTextExtent (const char text[], HDC dc /*= txDC()*/)
{
$1 SIZE size = {-1, -1};
$ if (_TX_ARGUMENT_FAILED (text)) return size;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return size;
$ size_t len = strlen (text);
$ txGDI ((Win32::GetTextExtentPoint32 (dc, text, (int) len, &size)), dc) asserted;
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetTextExtentX (const char text[], HDC dc /*= txDC()*/)
{
$1 return txGetTextExtent (text, dc) .cx;
}
//-----------------------------------------------------------------------------------------------------------------
int txGetTextExtentY (const char text[], HDC dc /*= txDC()*/)
{
$1 return txGetTextExtent (text, dc) .cy;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txSetTextAlign (unsigned align /*= TA_CENTER | TA_BASELINE*/, HDC dc /*= txDC()*/)
{
$1 if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return txGDI ((Win32::SetTextAlign (dc, align)), dc);
}
//-----------------------------------------------------------------------------------------------------------------
LOGFONT* txFontExist (const char name[])
{
$1 if (_TX_ARGUMENT_FAILED (name)) return NULL;
$ static LOGFONT font = {};
$ font.lfCharSet = DEFAULT_CHARSET;
$ strncpy_s (font.lfFaceName, sizeof (font.lfFaceName), name, sizeof (font.lfFaceName) - 1);
$ struct tools
{
static int CALLBACK enumFonts (const LOGFONT* fnt, const TEXTMETRIC*, DWORD, LPARAM data)
{
$ if (_TX_ARGUMENT_FAILED (fnt)) return 0;
$ if (_TX_ARGUMENT_FAILED (data)) return 0;
#ifndef __STRICT_ANSI__
$ return _strnicmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
#else
$ return strncmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
#endif
}
};
$ return txGDI ((Win32::EnumFontFamiliesEx (NULL, &font, tools::enumFonts, (LPARAM) &font, 0)), NULL) == 0? &font : NULL;
}
//-----------------------------------------------------------------------------------------------------------------
bool txSelectObject (HGDIOBJ obj, HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (obj)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ return _txBuffer_Select (obj, dc);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateCompatibleDC (double sizeX, double sizeY, HBITMAP bitmap /*= NULL*/)
{
$1 POINT size = { ROUND (sizeX), ROUND (sizeY) };
$ HDC dc = _txBuffer_Create (NULL, &size, bitmap);
$ assert (dc); if (!dc) return NULL;
$ txSetDefaults (dc);
$ if (!_txCanvas_UserDCs) return dc;
$ txAutoLock _lock;
$ _txCanvas_UserDCs->push_back (dc);
$ if (_txCanvas_UserDCs->size() >= _TX_BUFSIZE)
{$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загрузили уже %d HDC, системе может стать плохо.", (int) _txCanvas_UserDCs->size()); }
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateDIBSection (double sizeX, double sizeY, RGBQUAD** pixels /*= NULL*/)
{
$1 RGBQUAD* buf = NULL;
$ if (!pixels) pixels = &buf;
$ BITMAPINFO info = {{ sizeof (info), ROUND (sizeX), ROUND (sizeY), 1, WORD (sizeof (RGBQUAD) * 8), BI_RGB }};
$ HDC dc = txCreateCompatibleDC (0, 0, Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (void**) pixels, NULL, 0));
$ RGBQUAD black = { 0, 0, 0, 255 };
$ for (int i = 0; i < sizeX * sizeY; i++) (*pixels)[i] = black;
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
HDC txCreateDIBSection (double sizeX, double sizeY, COLORREF** pixels)
{
$1 return txCreateDIBSection (sizeX, sizeY, (RGBQUAD**) pixels);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txLoadImage (const char filename[], unsigned imageFlags /*= IMAGE_BITMAP*/, unsigned loadFlags /*= LR_LOADFROMFILE*/)
{
$1 if (_TX_ARGUMENT_FAILED (filename && *filename)) return NULL;
$ HBITMAP image = (HBITMAP) Win32::LoadImage ((loadFlags & LR_LOADFROMFILE)? NULL : GetModuleHandle (NULL),
filename, imageFlags, 0, 0, loadFlags);
$ if (!image) return NULL;
$ HDC dc = txCreateCompatibleDC (0, 0, image);
$ if (!(loadFlags & LR_LOADFROMFILE)) return dc;
$ static std::map <std::string, unsigned> loadTimes;
$ std::string file = filename;
$ unsigned time = GetTickCount();
$ if ((long) (time - loadTimes [file]) < _TX_TIMEOUT)
{$ txNotifyIcon (NIIF_WARNING, NULL, "Вы загружаете \"%s\" слишком часто, программа будет тормозить.", filename); }
$ loadTimes [file] = time;
$ return dc;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDeleteDC (HDC* pdc)
{
$1 if (_TX_ARGUMENT_FAILED (pdc)) return false;
$ HDC dc = *pdc;
$ bool ok = _txBuffer_Delete (pdc);
$ if (!ok) return false;
$ if (!_txCanvas_UserDCs) return ok;
$ txAutoLock _lock;
$ std::vector <HDC> ::iterator it = std::find (_txCanvas_UserDCs->begin(), _txCanvas_UserDCs->end(), dc);
$ if (it != _txCanvas_UserDCs->end()) { std::swap (*it, _txCanvas_UserDCs->back()); _txCanvas_UserDCs->pop_back(); }
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
bool txDeleteDC (HDC dc)
{
$1 return txDeleteDC (&dc);
}
//-----------------------------------------------------------------------------------------------------------------
bool txBitBlt (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, unsigned operation /*= SRCCOPY*/)
{
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
$ return txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), operation)), destImage);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txBitBlt (double xDest, double yDest, HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txBitBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource);
}
//-----------------------------------------------------------------------------------------------------------------
bool txTransparentBlt (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, COLORREF transColor /*= TX_BLACK*/)
{
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
#if !defined (NDEBUG)
$ if (!(0 <= xSource && xSource + width <= size.x &&
0 <= ySource && ySource + height <= size.y))
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
"функция txTransparentBlt() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y);
}
#endif
$ bool ok = (Win32::TransparentBlt != NULL);
$ if (ok)
{
$ ok &= txGDI (!!(Win32::TransparentBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), transColor)),
destImage);
}
else
{
$ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)),
destImage);
}
return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txTransparentBlt (double xDest, double yDest, HDC sourceImage,
COLORREF transColor /*= TX_BLACK*/, double xSource /*= 0*/, double ySource /*= 0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txTransparentBlt (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, transColor);
}
//-----------------------------------------------------------------------------------------------------------------
bool txAlphaBlend (HDC destImage, double xDest, double yDest, double width, double height,
HDC sourceImage, double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/)
{
// Это проверки того, правильные ли HDC вы передали в функцию.
// Не бойтесь долларов - <s>это не запрещенная валюта</s> это макросы для отладки TXLib'а.
$1 if (_TX_HDC_FAILED (destImage)) return false;
$ if (_TX_HDC_FAILED (sourceImage)) return false;
// Это автоматическое определение размеров картинки (точнее, HDC источника - source) с помощью txGetExtent().
$ POINT size = txGetExtent (sourceImage);
$ if (!width) width = size.x;
$ if (!height) height = size.y;
// Это проверка того, что картинка (или ее часть) правильно попадает в окно (точнее, HDC приемника - destination, dest).
// Если она "вылезает" из окна в любую сторону, то Win32::AlphaBlend не будет работать. Эта проверка происходит только
// в режиме отладки (когда не задан макрос NDEBUG - No Debugging, без отладки).
#if !defined (NDEBUG)
$ if (!(0 <= xSource && xSource + width <= size.x &&
0 <= ySource && ySource + height <= size.y))
{
$ SetLastError (ERROR_INVALID_DATA);
$ TX_ERROR ("Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
"функция txAlphaBlend() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (int) size.x, (int) size.y);
}
#endif
// Это на случай, если параметр alpha вылезает за диапазон [0..1].
$ if (alpha < 0) alpha = 0;
$ if (alpha > 1) alpha = 1;
// Об этом см. ниже.
$ BITMAP bmap = { 0, 0, 0, 0, 0, 24 };
$ bool ok = !!Win32::GetObject (txGDI ((Win32::GetCurrentObject (sourceImage, OBJ_BITMAP)), sourceImage), sizeof (bmap), &bmap);
//
// * Структура BLENDFUNCTION *
//
// Эта структура определяет, как цвета пикселей окна (точнее, source DC) и картинки смешиваются при рисовании с учетом
// прозрачности.
//
// Параметры смешивания помещаются в группу из переменных - так называемую структуру. Переменные, входящие в структуру,
// называются компонентами структуры.
//
// В этой структуре более всех важен ее третий компонент (см. ниже), задающий прозрачность картинки.
// Если он равен нулю - то картинка будет считаться полностью прозрачной и вызов Win32::AlphaBlend ничего не нарисует.
// Если он равен 255 - то картинка скопируется в окно полностью, без эффекта прозрачности.
//
// С помощью четвертого компонента структуры система рисования Windows (Win32 GDI) узнает, надо ли учитывать альфа-канал
// в копируемой картинке или его надо игнорировать. Если в картинке фактически есть правильно построенный альфа-канал
// (см. описание функции txAlphaBlend в системе помощи TXLib), то четвертый компонент структуры надо задать как константу
// AC_SRC_ALPHA.
//
// Если в картинке альфа-канала фактически нет, то четвертый параметр надо установить в 0.
//
// В коде ниже четвертый параметр определяется автоматически из вызова Win32::GetObject(), для универсальности применения
// функции txAlphaBlend().
//
// Пожалуйста, не надо бездумно копировать себе в программу этот код. Осмыслите его, и решите, будете ли вы использовать
// альфа-канал или нет, и установите четвертый параметр структуры либо AC_SRC_ALPHA, либо 0. Иначе этим копипастом вы
// породите невнятный паленый код и безнадежно испортите себе карму. :((
//
// На доллары ($) не обращайте внимания, они нужны для отладки библиотеки TXLib. В вашей программе их использовать не надо.
//
// ____1____ 2 ___________3___________ _______________________4________________________
// / \ | / \ / \.
$ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE) ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) };
//
// * Вызов стандартной функции Win32::AlphaBlend() *
//
// Ниже - это вызов Win32::AlphaBlend(). Он так устроен, что, если вдруг что-то не получилось (т.е. Win32::AlphaBlend()
// вернет 0), то тогда будет вызвана Win32::BitBlt(), которая просто скопирует картинку без учета прозрачности. Погуглите
// "Windows AlphaBlend function" и почитайте про ее параметры.
//
// Как видите, оригинальная функция из Win32 принимает размеры не только исходной, но и итоговой картинки, и если они не
// совпадают, то картинка будет уменьшена или увеличена. TXlib'овская <s>паленая</s> функция txAlphaBlend предполагает, что
// эти размеры всегда совпадают, и поэтому при работе с txAlphaBlend() масштаб будет всегда 1:1. <s>Так себе решение, но</s>
// это сделано для упрощения вызова функции txAlphaBlend().
$ if (Win32::AlphaBlend) // Только то, что эти параметры передаются одинаковыми, не дает возможность менять масштаб картинки! // <<--
{ // // vvvvv vvvvvv // <<--
$ ok &= txGDI (!!(Win32::AlphaBlend (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height), // <<==
sourceImage, ROUND (xSource), ROUND (ySource), ROUND (width), ROUND (height), blend)), // <<==
destImage); // ^^^^^ ^^^^^^ // <<--
} // ||||| |||||| // <<--
// См. "AlphaBlend function" в Google, ищите смысл параметров wSrc и wDest (hSrc и hDest). Думайте! // <<--
else
{
$ ok &= txGDI (!!(Win32::BitBlt (destImage, ROUND (xDest), ROUND (yDest), ROUND (width), ROUND (height),
sourceImage, ROUND (xSource), ROUND (ySource), SRCCOPY)),
destImage);
$ ok = false;
}
// В этой функции проверок и комментариев больше, чем рабочего кода, и это как бы намекает, что нетрудно сделать свою
// аналогичную функцию без ограничений масштаба отображения. <s>Если ты дочитал до этого места,</s> пересядь с иглы TXLib'а
// на поверхность GDI Win32, <s>хотя GDI тоже так себе, так что лучше заюзай GDI+, SFML, OpenGL или DirectX, будет круто.
// Хотя это и сложнее.</s>
// Но помни про паленый копипаст и карму, см. выше. Я предупредил.
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline bool txAlphaBlend (double xDest, double yDest, HDC sourceImage,
double xSource /*= 0*/, double ySource /*= 0*/, double alpha /*= 1.0*/)
{
$1 if (_TX_TXWINDOW_FAILED()) return false;
$ return txAlphaBlend (txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha);
}
//-----------------------------------------------------------------------------------------------------------------
HDC txUseAlpha (HDC image)
{
$1 if (_TX_HDC_FAILED (image)) return NULL;
$ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP);
$ if (!bitmap) return NULL;
$ DIBSECTION dib = {};
$ Win32::GetObject (bitmap, sizeof (dib), &dib) asserted;
$ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight };
$ BITMAPINFO info = {{ sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB }};
$ RGBQUAD* buf = NULL;
$ bool isDIB = (dib.dsBm.bmPlanes == 1 &&
dib.dsBm.bmBitsPixel == sizeof (RGBQUAD) * 8 &&
dib.dsBmih.biCompression == DIB_RGB_COLORS &&
dib.dsBm.bmBits);
$ if (!isDIB)
{
$ buf = new (std::nothrow) RGBQUAD [size.x * size.y];
$ if (!buf) return NULL;
$ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted;
}
else
{
$ buf = (RGBQUAD*) dib.dsBm.bmBits;
}
$ for (int y = 0; y < size.y; y++)
for (int x = 0; x < size.x; x++)
{
RGBQUAD* color = &buf [x + y * size.x]; // Get color at (x, y) within image buffer
color->rgbRed = (BYTE) ROUND (color->rgbRed * color->rgbReserved / 255.0);
color->rgbGreen = (BYTE) ROUND (color->rgbGreen * color->rgbReserved / 255.0);
color->rgbBlue = (BYTE) ROUND (color->rgbBlue * color->rgbReserved / 255.0);
}
$ if (!isDIB)
{
$ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS) asserted;
$ delete[] buf;
}
$ return image;
}
//-----------------------------------------------------------------------------------------------------------------
bool txSaveImage (const char filename[], HDC dc /*= txDC()*/)
{
$1 if (_TX_ARGUMENT_FAILED (filename)) return false;
$ if (_TX_DEFAULT_HDC_FAILED (dc)) return false;
$ POINT size = txGetExtent (dc);
$ size_t szHdrs = sizeof (BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER),
szImg = (size.x * size.y) * sizeof (RGBQUAD);
$ BITMAPFILEHEADER hdr = { 0x4D42 /* 'MB' */, (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs };
$ BITMAPINFOHEADER info = { sizeof (info), size.x, size.y, 1, (WORD) (sizeof (RGBQUAD) * 8), BI_RGB };
$ bool ok = true;
$ RGBQUAD* buf = new (std::nothrow) RGBQUAD [size.x * size.y];
$ ok &= (buf != NULL);
$ if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y,
buf, (BITMAPINFO*) &info, DIB_RGB_COLORS) asserted;
$ FILE* f = NULL;
$ if (ok) fopen_s (&f, filename, "wb");
$ ok &= (f != NULL);
$ if (ok) ok &= (fwrite (&hdr, sizeof (hdr), 1, f) == 1);
$ if (ok) ok &= (fwrite (&info, sizeof (info), 1, f) == 1);
$ if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1);
$ ok &= (f && fclose (f) == 0);
$ delete[] buf;
$ buf = NULL;
$ return ok;
}
//-----------------------------------------------------------------------------------------------------------------
inline void txRedrawWindow()
{
$1 txSleep (0);
}
//-----------------------------------------------------------------------------------------------------------------
inline int txUpdateWindow (int update /*= true*/)
{
$1 return _txCanvas_SetRefreshLock (update >= 0? !update : -update);
}
//-----------------------------------------------------------------------------------------------------------------
inline int txBegin()
{
$1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1);
$ return _txCanvas_RefreshLock;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txEnd()
{
$1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1);
$ return _txCanvas_RefreshLock;
}
//-----------------------------------------------------------------------------------------------------------------
double txSleep (double time)
{
$1 LARGE_INTEGER start = {};
$ QueryPerformanceCounter (&start) asserted;
$ LARGE_INTEGER freq = {};
$ QueryPerformanceFrequency (&freq) asserted;
$ int lock = _txCanvas_RefreshLock;
$ _txCanvas_RefreshLock = 0;
$ HWND wnd = txWindow();
if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
$ Sleep (ROUND ((time >= 0)? time : 0));
$ _txCanvas_RefreshLock = lock;
$ LARGE_INTEGER stop = {};
$ QueryPerformanceCounter (&stop) asserted;
$ return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart;
}
//-----------------------------------------------------------------------------------------------------------------
bool txLock (bool wait /*= true*/)
{
$0 if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
$ if (wait) {$ return EnterCriticalSection (&_txCanvas_LockBackBuf), true; }
else {$ return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); }
}
//-----------------------------------------------------------------------------------------------------------------
bool txUnlock()
{
$0 LeaveCriticalSection (&_txCanvas_LockBackBuf);
$ if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
$ return false;
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
inline T txUnlock (T value)
{
$1 txUnlock();
$ return value;
}
//-----------------------------------------------------------------------------------------------------------------
inline POINT txMousePos()
{
$1 POINT pos = {};
$ GetCursorPos (&pos);
$ if (txWindow())
{$ ScreenToClient (txWindow(), &pos); }
$ return pos;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txMouseX()
{
return txMousePos() .x;
}
//-----------------------------------------------------------------------------------------------------------------
inline int txMouseY()
{
return txMousePos() .y;
}
//-----------------------------------------------------------------------------------------------------------------
inline unsigned txMouseButtons()
{
$1 return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) | // MSB to bit 0
((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) | // MSB to bit 1
((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13); // MSB to bit 2
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txSetConsoleAttr (unsigned color /*= FOREGROUND_LIGHTGRAY*/)
{
unsigned oldAttr = txGetConsoleAttr();
SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color);
return oldAttr;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txGetConsoleAttr()
{
CONSOLE_SCREEN_BUFFER_INFO con = {};
GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
return con.wAttributes;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txSetConsoleCursorPos (double x, double y)
{
$1 POINT fontSz = txGetConsoleFontSize();
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ COORD pos = { (short) ROUND (1.0 * x / fontSz.x + con.srWindow.Left),
(short) ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) };
$ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos) asserted;
$ POINT prev = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
$ return prev;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleCursorPos()
{
$1 POINT fontSz = txGetConsoleFontSize();
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ POINT pos = { ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
$ return pos;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleExtent()
{
$1 CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con) asserted;
$ POINT size = { con.srWindow.Right - con.srWindow.Left + 1,
con.srWindow.Bottom - con.srWindow.Top + 1 };
$ return size;
}
//-----------------------------------------------------------------------------------------------------------------
bool txClearConsole()
{
$1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
$ CONSOLE_SCREEN_BUFFER_INFO con = {};
$ GetConsoleScreenBufferInfo (out, &con) asserted;
$ COORD start = {con.srWindow.Left, con.srWindow.Top};
$ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) *
(con.srWindow.Bottom - con.srWindow.Top + 1);
$ DWORD written = 0;
$ FillConsoleOutputCharacter (out, 0x20 /*' '*/, len, start, &written) asserted;
$ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written) asserted;
$ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start) asserted;
$ return written == len;
}
//-----------------------------------------------------------------------------------------------------------------
POINT txGetConsoleFontSize()
{
$1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}};
$ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE), false, &font));
$ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y };
$ txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1], "W", 1, &size), txDC());
$ POINT sizeFont = { size.cx, size.cy };
$ return sizeFont;
}
//-----------------------------------------------------------------------------------------------------------------
bool txTextCursor (bool blink /*= true*/)
{
$1 bool old = _txConsole_IsBlinking;
$ _txConsole_IsBlinking = blink;
$ return old;
}
//-----------------------------------------------------------------------------------------------------------------
bool txPlaySound (const char filename[] /*= NULL*/, DWORD mode /*= SND_ASYNC*/)
{
$1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT;
$ if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC;
$ if (!filename) mode = SND_PURGE;
$ return !!Win32::PlaySound (filename, NULL, mode);
}
//-----------------------------------------------------------------------------------------------------------------
int txSpeak (const char* text, ...)
{
$1 bool verbose = false; (void) verbose;
$ bool async = false; (void) async;
$ for (; text && *text; text++)
{
if (*text == '\a') {$ async = true; }
else if (*text == '\v') {$ verbose = true; }
else break;
}
$ char textA [_TX_BUFSIZE] = "You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!";
$ va_list arg; va_start (arg, text);
if (text && *text) {$ _tx_vsnprintf_s (textA, sizeof (textA) - 1, text, arg); }
$ va_end (arg);
#ifdef TX_USE_SPEAK
if (text && verbose) {$ printf ("%s", textA); }
$ int time = GetTickCount();
$ static wchar_t textW [_TX_BUFSIZE * sizeof (wchar_t)] = L"";
$ MultiByteToWideChar (_TX_CODEPAGE, 0, textA, -1, textW, sizearr (textW));
$ static ISpVoice* voice = NULL;
$ if (text && !voice)
{
$ HRESULT res = Win32::CoInitialize (NULL);
if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (void**) &voice); }
}
$ if (text && voice)
{
$ Win32::_fpreset();
$ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL);
$ tx_fpreset();
}
$ if (!text && voice)
{
$ voice->Release();
$ voice = NULL;
$ Win32::CoUninitialize();
}
$ return (voice)? GetTickCount() - time : -1;
#else
$ if (text)
{
$ unsigned oldAttr = txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK);
$ txNotifyIcon (NIIF_ERROR, "txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)", "\n" "%s", textA);
$ txSetConsoleAttr (oldAttr);
}
$ return -1;
#endif
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txPlayVideo (int x, int y, int width, int height, const char fileName[],
double zoom /*= 0*/, double gain /*= 1*/, HWND wnd /*= txWindow()*/)
{
$1 if (wnd && wnd == txWindow() && _TX_TXWINDOW_FAILED()) return -1;
$ int time = GetTickCount();
$ static char processUID [64] = "";
if (!*processUID)
{
$ FILETIME startTime = {}, null = {};
$ GetProcessTimes (GetCurrentProcess(), &startTime, &null, &null, &null) asserted;
$ _snprintf_s (processUID, sizeof (processUID) - 1, "TXLib[%08X%08X]::txPlayVideo",
(unsigned) startTime.dwHighDateTime, (unsigned) startTime.dwLowDateTime) < (int) sizeof (processUID) asserted;
}
$ if (!fileName)
{
$ txTaskKill ("vlc.exe", processUID, 0); // Kill'em all, by command line pattern
$ return 0;
}
$ static const char* vlcPath = _txPlayVideo_FindVLC();
$ if (!vlcPath || _access (vlcPath, 0) != 0)
{
$ static int once = false;
$ if (*fileName && !once++)
{$ txOutputDebugPrintf ("\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org "
"и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n"
"--\n" "Всегда Ваша, функция " /* как бы */ "txPlayVideo()...\n"
"P.S. См. мое описание в TXLib Help."); }
$ return INT_MIN;
}
$ bool async = false;
if (*fileName == '\a') {$ async = true; fileName++; }
$ RECT rect = {};
if (wnd) {$ GetClientRect (wnd, &rect); }
if (!width) {$ width = rect.right; }
if (!height) {$ height = rect.bottom; }
// Create a child window to hold the video stream
$ const char* errPos = "ВНЕЗАПНО";
$ volatile HWND child = NULL;
$ if (wnd && (wnd == txWindow()))
{
$ const char* wndClass = txRegisterClass ("txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1);
$ static int number = 1;
$ CREATESTRUCT createData = { NULL, NULL, (HMENU) (size_t) number++, txWindow(), height, width, y, x,
WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass };
$ child = txCreateExtraWindow (createData);
$ if (!child)
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Не могу создать окно для видео :("), errPos));
$ return INT_MIN+3;
}
$ BringWindowToTop (child);
$ wnd = child;
}
// Build the command line
if (!zoom && !wnd) {$ zoom = 1; }
$ char sZoom [64] = "--autoscale";
if (zoom) {$ _snprintf_s (sZoom, sizeof (sZoom) - 1, "--no-autoscale --zoom=%.10g", zoom) < (int) sizeof (sZoom) asserted; }
$ static char cmd [MAX_PATH*2 + 1024] = "";
$ _snprintf_s (cmd, sizeof (cmd) - 1, "\"%s\" \"%s\" vlc://quit"
" %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s"
" --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging"
" --ignore-config --reset-config --no-one-instance --play-and-exit"
" --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file"
" --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events",
vlcPath, (*fileName? fileName : "fileName"), sZoom, gain, (uint64_t) wnd, processUID, _txLogName) < (int) sizeof (cmd) asserted;
$ txOutputDebugPrintf ("txPlayVideo (%d, %d, %d, %d, \"%s\", %lg, %lg, %p): [%s]\n\n",
x, y, width, height, fileName, zoom, gain, wnd, cmd);
$ if (!*fileName)
{
if (child) {$ txDestroyWindow (child); }
$ return (intptr_t) cmd;
}
$ if (!strstr (fileName, "://") && _access (fileName, 0) != 0)
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "\n" "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Не найден файл \"%s\"", fileName), errPos));
if (child) {$ txDestroyWindow (child); }
$ return INT_MIN+1;
}
// Run VLC, run
$ PROCESS_INFORMATION vlc = {};
$ STARTUPINFO start = { sizeof (start) };
$ DWORD ret = 0;
$ if (CreateProcess (NULL, cmd, NULL, NULL, true, 0, NULL, NULL, &start, &vlc) &&
vlc.hProcess && vlc.hThread)
{
$ if (child)
{
$ assert (wnd == child);
$ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess);
}
$ if (!async)
{
$ WaitForSingleObject (vlc.hProcess, INFINITE);
$ GetExitCodeProcess (vlc.hProcess, &ret) asserted;
}
$ if (!child)
{
$ CloseHandle (vlc.hProcess) asserted;
}
$ CloseHandle (vlc.hThread) asserted;
$ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (int) ret);
}
else
{
$ txNotifyIcon (NIIF_ERROR, "txPlayVideo() сообщает", "%s",
strstr (_txError (NULL, 0, NULL, 0, "\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos));
$ if (child)
{$ txDestroyWindow (child); }
$ return INT_MIN+4;
}
#undef PROCESS_UID_
}
//-----------------------------------------------------------------------------------------------------------------
inline intptr_t txPlayVideo (const char fileName[], double zoom /*= 0*/, double gain /*= 0*/, HWND wnd /*= txWindow()*/)
{
$1 return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd);
}
//-----------------------------------------------------------------------------------------------------------------
LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
{
const UINT_PTR checkTimer = 1;
switch (msg)
{
case WM_CREATE:
{
$1 SetTimer (wnd, checkTimer, 5*_txWindowUpdateInterval, NULL) asserted;
}
break;
case WM_DESTROY:
{
$1 KillTimer (wnd, checkTimer) asserted;
$ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
$ if (vlc)
{
$ Win32::TerminateProcess (vlc, 0);
$ CloseHandle (vlc) asserted;
$ SetWindowLongPtr (wnd, GWLP_USERDATA, 0);
}
}
break;
case WM_TIMER:
{
HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT)
{
$1 DestroyWindow (wnd) asserted;
}
}
break;
default:
break;
}
return DefWindowProc (wnd, msg, wpar, lpar);
}
//-----------------------------------------------------------------------------------------------------------------
const char* _txPlayVideo_FindVLC()
{
$1 static char vlcPath [MAX_PATH] = "";
$ if (SearchPath (NULL, "vlc.bat", NULL, sizeof (vlcPath), vlcPath, NULL))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (SearchPath (NULL, "vlc.exe", NULL, sizeof (vlcPath), vlcPath, NULL))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath, sizeof (vlcPath)))
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ if (txRegQuery ("HKLM\\Software\\VideoLAN\\VLC", "InstallDir", vlcPath, sizeof (vlcPath)))
{
$ strncat_s (vlcPath, sizeof (vlcPath) - 1, "\\vlc.exe", INT_MAX);
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ strncpy_s (vlcPath, sizeof (vlcPath), "C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
{
if (_access (vlcPath, 0) == 0) {$ return vlcPath; }
}
$ return NULL;
}
//-----------------------------------------------------------------------------------------------------------------
// +--<<< Это вряд ли имеет отношение к тому, что вы ищете :)
// V Полезно смотреть не только вверх, но и вниз
WNDPROC txSetWindowsHook (WNDPROC wndProc /*= NULL*/)
{
$1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc;
$ return old;
}
//-----------------------------------------------------------------------------------------------------------------
// +--<<< А это, наконец, искомое определение этой функции.
// | Смотрите по сторонам! Нужная вам функция где-то рядом.
// |
// v
bool txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()
{
txMessageBox ("Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n"
"txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n"
"Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. "
"Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n"
"Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом "
"с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n"
"Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы "
"установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n",
"Не получилось", MB_ICONSTOP);
// The truth is out there... (C++files)
return false;
}
//-----------------------------------------------------------------------------------------------------------------
// Bingo! Now you are learned to use the Sources, Luke. And may the Source be with you.
inline bool txDisableAutoPause()
{
_txExit = true;
return true;
}
// P.S. This library contains more undocumented functions. Search them via "Luke" keyword.
//-----------------------------------------------------------------------------------------------------------------
void _txDump (const void* address, const char name[] /*= "_txDump()"*/, bool pause /*= true*/)
{
$1 assert (!_txIsBadReadPtr (address));
const unsigned char* p = (const unsigned char*) address;
$ unsigned x = 0;
$ unsigned attr = txGetConsoleAttr();
$ txSetConsoleAttr (FOREGROUND_WHITE);
$ printf ("\n%*.*s ", (int) sizeof (address) * 2, (int) sizeof (address) * 2, ((name)? name : ""));
$ txSetConsoleAttr (FOREGROUND_YELLOW);
$ for (x = 0; x < 16; x++) printf ("%02X ", x);
$ for (x = 0; x < 16; x++) printf ("%X", x);
$ const char isCtrl[] = "\a" "\b" "\n" "\r" "\t" "\0";
$ const char xlatCh[] = "·" "·" "·" "·" "·" "·";
$ const size_t szCtrl = sizeof (isCtrl) - 1;
$ int oldCP = GetConsoleOutputCP();
$ for (int y = 0; y < 16; y++, p += 16)
{
txSetConsoleAttr (FOREGROUND_YELLOW);
printf ("\n" "%*p ", (int) sizeof (address) * 2, p);
int color = FOREGROUND_LIGHTGREEN;
for (x = 0; x < 16; x++)
{
txSetConsoleAttr (color + x/4%2);
printf ("%02X ", p[x]);
}
for (x = 0; x < 16; x++)
{
txSetConsoleAttr (color + x/4%2);
char c = p[x];
const char* ctrl = (const char*) memchr (isCtrl, c, szCtrl);
if (ctrl) c = xlatCh [ctrl - isCtrl];
if (ctrl) SetConsoleOutputCP (1251);
printf ("%c", c);
if (ctrl) SetConsoleOutputCP (oldCP);
}
}
$ txSetConsoleAttr (attr);
$ printf ("\n");
$ if (pause)
{
$ SetConsoleOutputCP (_TX_CODEPAGE);
$ fprintf (stderr, "[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP);
$ (void)_getch();
$ SetConsoleOutputCP (oldCP);
}
}
//-----------------------------------------------------------------------------------------------------------------
void _txStackBackTrace (const char file[] /*= "?"*/, int line /*= 0*/, const char func[] /*= "?"*/,
bool readSource /*= true*/)
{
$1 unsigned attr = txGetConsoleAttr();
$ txSetConsoleAttr (FOREGROUND_LIGHTCYAN);
$ fprintf (stderr, "\n" "--------------------------------------------------\n"
"Трассировка стека из \"%s\" at %s (%d):\n\n"
"%s\n\n"
"--------------------------------------------------\n\n",
func, file, line, _txCaptureStackBackTrace (1, readSource));
$ txSetConsoleAttr (attr);
}
//-----------------------------------------------------------------------------------------------------------------
char* txDemangle (const char* mangledName, std::nomeow_t)
{
$1 if (!mangledName) return NULL;
$ char* typeName = NULL;
#if defined (_GCC_VER)
$ int err = 1;
$ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err;
if (typeName) {$ return typeName; }
#endif
$ unsigned short flags = 0;
$ if (mangledName[0] == '.')
{
$ mangledName++;
$ flags = 0x2800; // UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY
}
$ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags));
if (typeName) {$ return typeName; }
$ return _strdup (mangledName);
}
//-----------------------------------------------------------------------------------------------------------------
std::string txDemangle (const char* mangledName)
{
$1 char* typeName = txDemangle (mangledName, std::nomeow);
$ std::string name (typeName? typeName : "");
$ free (typeName);
$ return name;
}
//-----------------------------------------------------------------------------------------------------------------
double txQueryPerformance()
{
$1 int maxTime = 500;
$ int maxSamples = 100;
$ POINT size = {100, 100};
$ HDC dc = _txBuffer_Create (txWindow(), &size, NULL);
$ assert (dc); if (!dc) return -1;
$ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1);
$ assert (mask);
$ LARGE_INTEGER freq = {};
$ QueryPerformanceFrequency (&freq) asserted;
$ LARGE_INTEGER start = {};
$ QueryPerformanceCounter (&start) asserted;
$ int samples = 0;
$ while (samples++ < maxSamples)
{
$ LARGE_INTEGER cur = {};
$ QueryPerformanceCounter (&cur) asserted;
$ double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart;
$ if (t > maxTime) break;
// Draw test scene
$ for (int y = 0; y < size.y; y++)
for (int x = 0; x < size.x; x++) txSetPixel (x, y, TX_BLACK, dc);
$ for (int y = 0; y < size.y; y += 10)
for (int x = 0; x < size.x; x += 50) txTextOut (x, y, "*", dc);
$ txEllipse (0, 0, size.x, size.y, dc);
$ txFloodFill (size.x/2, size.y/2, TX_TRANSPARENT, FLOODFILLSURFACE, dc);
$ txBitBlt (dc, size.x/2, 0, size.x/2, size.y/2, dc, 0, 0) asserted;
$ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2) asserted;
$ txBitBlt (dc, 0, size.y/2, size.x/2, size.y/2, dc, 0, 0) asserted;
$ txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0) asserted;
}
$ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask);
$ assert (mask);
$ _txBuffer_Delete (&dc);
$ return 3.0 * samples / sqrt (1.0 * size.x * size.y);
}
//-----------------------------------------------------------------------------------------------------------------
#if defined (_TX_CPP11)
template <int txFramesToAverage>
#endif
double txGetFPS (int minFrames)
{
$1 static LARGE_INTEGER time0 = {}; if (!time0.QuadPart) QueryPerformanceCounter (&time0);
$ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time);
$ if (time.QuadPart - time0.QuadPart == 0)
{$ return 0; }
$ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq);
$ double fps = (double) freq.QuadPart / (double) (time.QuadPart - time0.QuadPart);
$ time0 = time;
$ if (txFramesToAverage == 0) return fps;
$ static double average [txFramesToAverage] = {};
$ static unsigned n = 0;
$ average [n++ % txFramesToAverage] = fps;
$ unsigned nn = MIN (n, (unsigned) sizearr (average));
$ static double median [txFramesToAverage] = {};
$ std::copy (average, average + nn, median);
$ std::nth_element (median, median + nn/2, median + nn);
$ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0;
$ return ((int)n >= MIN (minFrames, txFramesToAverage))? fps : 0;
}
//-----------------------------------------------------------------------------------------------------------------
unsigned txExtractColor (COLORREF color, COLORREF component)
{
$1 switch (component)
{
case TX_RED:
case TX_HUE: $ return (color >> 0) & 0xFF;
case TX_GREEN:
case TX_SATURATION: $ return (color >> 8) & 0xFF;
case TX_BLUE:
case TX_LIGHTNESS: $ return (color >> 16) & 0xFF;
default: $ return CLR_INVALID;
}
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txRGB2HSL (COLORREF rgbColor)
{
$1 int r = (int) txExtractColor (rgbColor, TX_RED),
g = (int) txExtractColor (rgbColor, TX_GREEN),
b = (int) txExtractColor (rgbColor, TX_BLUE);
$ double m1 = MAX (MAX (r, g), b) / 255.0,
m2 = MIN (MIN (r, g), b) / 255.0,
dm = m1 - m2,
sm = m1 + m2,
ir = r / 255.0,
ig = g / 255.0,
ib = b / 255.0,
ih = 0,
is = 0,
il = sm / 2;
$ const double prec = 0.001;
$ if (fabs (dm) < prec)
{
$ is = dm / ((sm <= 1)? sm : (2-sm));
$ double cr = (m1 - ir) / dm,
cg = (m1 - ig) / dm,
cb = (m1 - ib) / dm;
$ if (fabs (ir - m1) < prec) ih = cb - cg;
$ if (fabs (ig - m1) < prec) ih = 2 + cr - cb;
$ if (fabs (ib - m1) < prec) ih = 4 + cg - cr;
}
$ ih = (ih >= 0)? ih*60 : ih*60 + 360;
$ return RGB (ROUND (ih / 360 * 255), ROUND (is * 255), ROUND (il * 255));
}
//-----------------------------------------------------------------------------------------------------------------
COLORREF txHSL2RGB (COLORREF hslColor)
{
$1 struct xRGB
{
static double calc (double h, double m1, double m2)
{
$ if (h < 0) h += 360;
$ if (h > 360) h -= 360;
$ return (h < 60)? m1 + (m2-m1) * h / 60 :
(h < 180)? m2 :
(h < 240)? m1 + (m2-m1) * (240-h) / 60 :
m1;
}
};
$ int h = (int) txExtractColor (hslColor, TX_HUE),
s = (int) txExtractColor (hslColor, TX_SATURATION),
l = (int) txExtractColor (hslColor, TX_LIGHTNESS);
$ double ih = h / 255.0 * 360.0,
il = l / 100.0,
is = s / 100.0,
m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is,
m1 = 2 * il - m2,
ir = s? xRGB::calc (ih + 120, m1, m2) : il,
ig = s? xRGB::calc (ih, m1, m2) : il,
ib = s? xRGB::calc (ih - 120, m1, m2) : il;
$ return RGB (ROUND (ir * 255), ROUND (ig * 255), ROUND (ib * 255));
}
//-----------------------------------------------------------------------------------------------------------------
inline double random (std::nomeow_t, double left, double right)
{
return left + (right - left) * ((double) rand() / RAND_MAX);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename Tx, typename Ta, typename Tb>
inline bool In (std::nomeow_t, Tx x, Ta a, Tb b)
{
return a <= x && x <= b;
}
//-----------------------------------------------------------------------------------------------------------------
inline int random (int range)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return rand() % range;
}
//-----------------------------------------------------------------------------------------------------------------
inline double random (double left, double right)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return random (std::nomeow, left, right);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename Tx, typename Ta, typename Tb>
inline bool In (Tx x, Ta a, Tb b)
{
if (rand() % 100 == 0) fprintf (stderr, "%.4s ", (const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
return In (std::nomeow, x, a, b);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool In (const POINT& pt, const RECT& rect)
{
if (_TX_ARGUMENT_FAILED (&pt)) return false;
if (_TX_ARGUMENT_FAILED (&rect)) return false;
return In (std::nomeow, pt.x, rect.left, rect.right) &&
In (std::nomeow, pt.y, rect.top, rect.bottom);
}
//-----------------------------------------------------------------------------------------------------------------
inline bool In (const COORD& pt, const SMALL_RECT& rect)
{
if (_TX_ARGUMENT_FAILED (&pt)) return false;
if (_TX_ARGUMENT_FAILED (&rect)) return false;
return In (std::nomeow, pt.X, rect.Left, rect.Right) &&
In (std::nomeow, pt.Y, rect.Top, rect.Bottom);
}
//-----------------------------------------------------------------------------------------------------------------
void tx_fpreset()
{
$1 txAutoLock _lock;
$ Win32::_fpreset();
$ unsigned new87 = 0x0008001C; // _EM_INVALID | _EM_DENORMAL | _EM_ZERODIVIDE | _EM_OVERFLOW | _EM_UNDERFLOW
#if !defined (__CYGWIN__)
$ unsigned old87 = 0;
$ if (_controlfp_s (&old87, 0, 0) == 0)
{$ (void) _controlfp_s (&old87, old87 & ~new87, 0x0008001F); } // _MCW_EM
#else
$ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F); // _MCW_EM
#endif
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
inline T zero() { T __zero = {}; return __zero; }
//}
//=================================================================================================================
//=================================================================================================================
//{ txPrintf() implementation
// Реализация txPrintf()
//=================================================================================================================
#if defined (_TX_CPP11)
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args);
template <typename T, typename... ArgsT> void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args);
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt);
template <typename T> void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg);
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg);
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt);
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg, ArgsT... args)
{
$1 assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%') {$}
else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '*') {$ stream << std::setw (width); }
else {$ TX_ERROR ("\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, precision_t prec, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '*') {$ stream << std::setprecision (prec+1); }
else {$ TX_ERROR ("\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T, typename... ArgsT>
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt, width_t width, precision_t prec, const T& arg, ArgsT... args)
{
$1 assert (&stream);
$ assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (fmt[0] == '%' && fmt[1] == '*' && fmt[2] == '.' && fmt[3] == '*') {$ stream << std::setw (width) << std::setprecision (prec+1); }
else {$ TX_ERROR ("\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt, txTypename (arg), n, format); }
$ _txPrintV (stream, format, n, fmt, arg);
$ _txPrintF (stream, format, n+1, fmt, args...);
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintF (std::ostringstream& stream, const char* format, int n, const char*& fmt)
{
$1 assert (fmt);
$ _txPrintV (stream, format, n, fmt);
if (!fmt[0]) {$}
else {$ TX_ERROR ("No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); }
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintV (std::ostringstream& stream, const char*, int, const char*& fmt)
{
$1 assert (&stream);
$ assert (fmt);
$ while (*fmt)
{
if (fmt[0] == '%')
{
if (fmt[1] == '%') fmt++;
else break;
}
stream << *fmt++;
}
$ }
//-----------------------------------------------------------------------------------------------------------------
template <typename T>
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, const T& arg)
{
$1 assert (&stream);
$ assert (fmt);
$ if (_TX_ARGUMENT_FAILED (&arg)) return;
if (fmt[0] == '%') {$}
else {$ TX_ERROR ("\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt, txTypename (arg), n, format); }
$ fmt++;
$ char oldFill = stream.fill (' ');
$ std::ios_base::fmtflags oldFlags = stream.flags();
$ for (;;) switch (*fmt)
{
case '-': $ stream << std::left; fmt++; break;
case '+': $ stream << std::showpos; fmt++; break;
case ' ': $ stream.fill (' '); fmt++; break;
case '#': $ stream << std::showbase; fmt++; break;
case '0': $ stream.fill ('0'); fmt++; break;
default: $ goto end;
}
end:
$ int width = (*fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0);
$ int prec = (*fmt == '.')? (*++fmt != '*')? (int) strtoul (fmt, const_cast <char**> (&fmt), 10) : (fmt++, 0) : 0;
if (width) {$ stream << std::setw (width); }
if (prec) {$ stream << std::setprecision (prec); }
$ fmt += strspn (fmt, "hljztL");
$ switch (*fmt)
{
case '$':
case '?': $ break;
case 'd':
case 'i':
case 'u': $ stream << std::dec; break;
case 'o': $ stream << std::oct; break;
case 'x': $ stream << std::hex; break;
case 'X': $ stream << std::hex << std::uppercase; break;
case 'f': $ stream << std::fixed; break;
case 'F': $ stream << std::fixed << std::uppercase; break;
case 'e': $ stream << std::scientific; break;
case 'E': $ stream << std::scientific << std::uppercase; break;
case 'g': $ break;
case 'G': $ stream << std::uppercase; break;
case 'a': $ break;
case 'A': $ stream << std::uppercase; break;
case 'c':
case 's':
case 'p': $ break;
default: $ TX_ERROR ("Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt, txTypename (arg), n, format); break;
}
$ fmt++;
if (&arg) {$ stream << arg; }
else {$ stream << "(null)"; }
$ stream.fill (oldFill);
$ stream.flags (oldFlags);
}
//-----------------------------------------------------------------------------------------------------------------
void _txPrintV (std::ostringstream& stream, const char* format, int n, const char*& fmt, int* arg)
{
$1 assert (fmt);
if (_TX_ARGUMENT_FAILED (arg)) return;
if (fmt[0] == '%' && fmt[1] == 'n') {$}
else {$ TX_ERROR ("\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); }
$ *arg = (int) stream.str().length();
$ fmt += 2;
}
//-----------------------------------------------------------------------------------------------------------------
template <typename T> inline const T& _txPrintfNormalizeArg (const T& arg) { if (_TX_ARGUMENT_FAILED (&arg)) {;} return arg; }
inline const char* _txPrintfNormalizeArg (const std::string& arg) { if (_TX_ARGUMENT_FAILED (&arg)) return NULL; return arg.c_str(); }
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (std::ostringstream& stream, const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&stream)) return 0;
$ if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ const char* fmt = format;
$ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...);
$ return (int) stream.str().length();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (char buffer[], size_t size, const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&buffer)) return 0;
$ if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ if (size > 0) size--;
$ buffer[size] = 0;
$ if (!size) return 0;
$ std::ostringstream stream;
$ stream.rdbuf() -> pubsetbuf (buffer, size);
$ txPrintf (stream, format, args...);
$ return (int) stream.str().length();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline std::string txFormat (const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&format)) return "";
$ std::ostringstream stream;
$ txPrintf (stream, format, args...);
$ return stream.str();
}
//-----------------------------------------------------------------------------------------------------------------
template <typename... ArgsT>
inline int txPrintf (const char* format, ArgsT... args)
{
$1 if (_TX_ARGUMENT_FAILED (&format)) return 0;
$ return printf ("%s", txFormat (format, args...) .c_str());
}
#endif
//-----------------------------------------------------------------------------------------------------------------
int _txPrintfCheck (const char* format, ...) tx_printfy (1);
inline int _txPrintfCheck (const char*, ...) { return 0; }
//}
//=================================================================================================================
//=================================================================================================================
//{ txDialog methods implementation
// Реализация методов класса txDialog
//
// See [1] http://msdn.microsoft.com/ru-ru/library/windows/desktop/ms645389%28v=vs.85%29.aspx
// [2] http://blogs.msdn.microsoft.com/oldnewthing/20050429-00/?p=35743
// [3] http://blogs.msdn.microsoft.com/oldnewthing/20040623-00/?p=38753
//=================================================================================================================
txDialog::txDialog () :
layout_ (NULL)
{$1}
//-----------------------------------------------------------------------------------------------------------------
txDialog::txDialog (const Layout* layout) :
layout_ (layout)
{$1}
//-----------------------------------------------------------------------------------------------------------------
const txDialog::Layout* txDialog::setLayout (const Layout* layout)
{
$1 assert (layout);
$ return ::std::swap (layout_, layout), layout;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txDialog::dialogBox (WORD resourceID)
{
$1 const char* resName = (char*)(uintptr_t) resourceID;
$ if (!FindResource (NULL, resName, RT_DIALOG)) return TX_DEBUG_ERROR ("Не найден ресурс диалога %d", resourceID), 0;
$ return DialogBoxParam (NULL, resName, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t txDialog::dialogBox (const txDialog::Layout* layout /*= NULL*/, size_t bufsize /*= 0*/)
{
$1 if (!layout) layout = layout_;
$ if (!layout) return TX_DEBUG_ERROR ("Не установлен динамический шаблон диалога"), 0;
$ if (!bufsize) bufsize = 1024;
$ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize);
$ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0;
$ const Layout* dlg = &layout[0];
$ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 };
$ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize,
(dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0,
dlg->x, dlg->y, dlg->sx, dlg->sy,
dlg->caption? dlg->caption : def.caption,
dlg->font? dlg->font : def.font,
dlg->fontsize? dlg->fontsize : def.fontsize, NULL);
$ WORD i = 0;
$ for (i = 1; layout[i].wndclass != END; ++i)
{
$ const Layout* item = &layout[i];
$ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl),
item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy,
item->id, (const char*)(uintptr_t) item->wndclass, item->caption);
}
$ tmpl->cdit = (unsigned short) (i-1);
$ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
$ GlobalFree (tmpl);
$ return res;
}
//-----------------------------------------------------------------------------------------------------------------
int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM)
{
$1 switch (msg)
{
case WM_INITDIALOG: $ SetForegroundWindow (wnd);
$ break;
case WM_COMMAND: $ switch (LOWORD (wParam))
{
case IDOK:
case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow());
$ EndDialog (wnd, (uintptr_t) this);
$ break;
default: $ break;
}
$ break;
default: $ break;
}
$ return FALSE;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
$1 static txDialog* this__ = NULL;
$ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam;
$ if (!this__) return FALSE;
$ return this__-> dialogProc (wnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle,
WORD controls, short x, short y, short cx, short cy,
const char caption[], const char font[], WORD fontsize, const char menu[])
{
$1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL;
$ WORD* pw = (WORD*) globalMem;
$ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->cdit = controls;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ if (menu > (const char*) 0xFFFF)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
else
{
$ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0);
$ *pw++ = (WORD)(uintptr_t) menu;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ if (style & DS_SETFONT)
{
$ *pw++ = fontsize;
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ return pw;
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle,
short x, short y, short cx, short cy,
WORD id, const char wclass[], const char caption[])
{
$1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL;
$ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary
$ ((ULONG&) pw) += 3;
$ ((ULONG&) pw) >>= 2;
$ ((ULONG&) pw) <<= 2;
$ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ tmpl->id = id;
$ if (HIWORD (wclass) == 0xFFFF)
{
$ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass));
$ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass));
}
else if (wclass)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ *pw++ = 0;
$ return pw;
}
//}
//=================================================================================================================
//=================================================================================================================
//{ Cleaning up the utility macros
// Очистка служебных макросов
//=================================================================================================================
#undef $
#undef $0
#undef $1
#undef $2
#undef $3
#undef $4
#undef $5
#undef $6
#undef $7
#undef $8
#undef $9
#undef $$
//}
//=================================================================================================================
//! @endcond
//=================================================================================================================
//{ Experimental Debugging macros
//! @name Экспериментальные отладочные макросы
//=================================================================================================================
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Misc
//! @brief Отладочная печать переменной во время вычисления выражения или участка кода
//! во время его выполнения.
//!
//! Сделай приятными твои <i>круглые сутки, MB_ICONEXCLAMATION);
11981 intptr_t _tx_snprintf_s (
char stream[], intptr_t size,
const char format[], ...)
11983 if (!format)
return 0;
11985 va_list arg; va_start (arg, format);
11986 intptr_t ret = _tx_vsnprintf_s (stream, size, format, arg);
11994 intptr_t _tx_vsnprintf_s (
char stream[], intptr_t size,
const char format[], va_list arg)
11996 if (!stream || !format)
return 0;
11998 #if defined (_TRUNCATE)
11999 intptr_t ret =
_vsnprintf_s (stream, size, _TRUNCATE, format, arg);
12001 intptr_t ret = _vsnprintf (stream, size, format, arg);
12004 if (ret < 0 && size >= 4)
12006 const char ellipsis[] =
"...";
12007 size_t szEllipsis =
sizeof (ellipsis) - 1;
12009 strncpy_s (stream + size - szEllipsis, szEllipsis+1, ellipsis, szEllipsis);
12012 return (ret >= 0)? ret : size;
12017 #if defined (__CYGWIN__)
12021 termios oldattr = {}; tcgetattr (STDIN_FILENO, &oldattr);
12023 termios newattr = oldattr;
12024 newattr.c_lflag &= ~(ICANON | ECHO);
12025 tcsetattr (STDIN_FILENO, TCSANOW, &newattr);
12027 int ch = getchar();
12029 tcsetattr (STDIN_FILENO, TCSANOW, &oldattr);
12036 int _putch (
int ch)
12038 termios old = {}; tcgetattr (STDOUT_FILENO, &old);
12041 cur.c_lflag &= ~ICANON;
12042 cur.c_lflag |= ECHO;
12043 tcsetattr (STDOUT_FILENO, TCSANOW, &cur);
12047 tcsetattr (STDOUT_FILENO, TCSANOW, &old);
12056 termios old = {}; tcgetattr (STDIN_FILENO, &old);
12059 cur.c_lflag &= ~(ICANON | ECHO);
12060 cur.c_cc[VMIN] = 1;
12061 cur.c_cc[VTIME] = 0;
12063 tcsetattr (STDIN_FILENO, TCSAFLUSH, &cur);
12065 fd_set fd = {}; FD_SET (STDIN_FILENO, &fd);
12068 int res = select (STDIN_FILENO + 1, &fd, NULL, NULL, &tv) && FD_ISSET (STDIN_FILENO, &fd);
12070 tcsetattr (STDIN_FILENO, TCSAFLUSH, &old);
12086 static char name[MAX_PATH] =
"";
12090 if (!GetModuleFileName (NULL, name,
sizeof (name) - 1)) *name = 0;
12092 char* ext = strrchr (name,
'.');
12093 if (ext)
_strlwr_s (ext,
sizeof (name) - 1 - (ext - name));
12098 if (fileNameOnly)
return name;
12100 static char fullName[MAX_PATH] =
"";
12101 strncpy_s (fullName,
sizeof (fullName), name,
sizeof (fullName) - 1);
12103 char* title = strrchr (fullName,
'\\');
if (!title) title = fullName;
12104 char* ext = strrchr (fullName,
'.');
if (!ext) ext = fullName + strlen (fullName);
12106 size_t sz =
sizeof (fullName) - (ext - fullName);
12114 const char* _txAppInfo()
12116 $1 time_t timeT = time (NULL) - clock()/CLOCKS_PER_SEC;
12117 char timeS[32] =
"";
12118 ctime_s (timeS,
sizeof (timeS), &timeT);
12121 char cwd [MAX_PATH] =
"";
12123 _tx_snprintf_s (text,
sizeof (text) - 1,
12125 "Developed with:\n\n"
12126 "The Dumb Artist Library (TX Library)\n"
12128 "See license on: http://txlib.ru\n\n"
12130 "TXLib file:" "\t" __FILE__
"\n"
12132 "Started:" "\t" "%.6s %.4s %.8s\n\n"
12134 "Run file:" "\t" "%s\n"
12135 "Directory:" "\t" "%s",
12137 #
if defined (_MSC_VER)
12139 #elif defined (__CYGWIN__)
12141 #elif defined (_GCC_VER) && defined (_WIN64)
12142 __mingw_get_crt_info(),
12144 "MinGW Runtime " TX_QUOTE (__MINGW32_MAJOR_VERSION)
"." TX_QUOTE (__MINGW32_MINOR_VERSION),
12146 (
sizeof (
void*) ==
sizeof (DWORD))? 32 : 64,
12148 timeS + 4, timeS + 20, timeS + 11,
12150 _getcwd (cwd,
sizeof (cwd) - 1));
12183 $0
return _txCanvas_Window;
12190 $0
return _txCanvas_BackBuf[0];
12197 return _txCanvas_Pixels;
12204 $0
static POINT err = {-1, -1};
12206 if (!dc) {$ POINT screen = { GetSystemMetrics (SM_CXSCREEN), GetSystemMetrics (SM_CYSCREEN) };
return screen; };
12208 if (_TX_DEFAULT_HDC_FAILED (dc)) {$
return err; }
12210 $ BITMAP bmap = {};
12211 $
txGDI (Win32::GetObject (Win32::GetCurrentObject (dc, OBJ_BITMAP),
sizeof (bmap), &bmap), dc)
asserted;
12213 $ POINT size = { bmap.bmWidth, bmap.bmHeight };
12235 $1
if (!wnd || !
txWindow())
return false;
12239 $
return !!SendNotifyMessage (
txWindow(), _TX_WM_DESTROYWND, 0, (LPARAM) wnd);
12242 $
if (SendNotifyMessage (
txWindow(), (_txMain? WM_CLOSE : WM_DESTROY), 0, 0) == 0)
return false;
12246 $ txNotifyIcon (NIIF_WARNING, NULL,
"\n" "Очень, очень плохо завершать программу через txDestroyWindow().\n\n"
12247 "Возвращайтесь через main(), там вам будут рады.\n");
12253 $
return _txCanvas_Window == NULL;
12258 HPEN
txSetColor (COLORREF color,
double thickness , HDC dc )
12260 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return NULL;
12262 $ HPEN pen = Win32::CreatePen ((color ==
TX_TRANSPARENT? PS_NULL : PS_SOLID),
ROUND (thickness), color);
12264 $
if (!pen)
return (HPEN) NULL;
12266 $
if (!_txBuffer_Select (pen, dc))
12268 $ Win32::DeleteObject (pen);
12269 $
return (HPEN) NULL;
12272 $
if (
txGDI (Win32::SetTextColor (dc, color), dc) == CLR_INVALID)
12273 {$
return (HPEN) NULL; }
12280 COLORREF txColor (
double red,
double green,
double blue)
12282 $1
if (red > 1) red = 1;
if (red < 0) red = 0;
12283 $
if (green > 1) green = 1;
if (green < 0) green = 0;
12284 $
if (blue > 1) blue = 1;
if (blue < 0) blue = 0;
12286 $ COLORREF color = RGB (
ROUND (red * 255),
ROUND (green * 255),
ROUND (blue * 255));
12288 $
return txSetColor (color)? color : CLR_INVALID;
12295 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return CLR_INVALID;
12297 $ HGDIOBJ obj =
txGDI ((Win32::GetCurrentObject (dc, OBJ_PEN)), dc);
12298 $
assert (obj);
if (!obj)
return CLR_INVALID;
12300 $
union { EXTLOGPEN extLogPen; LOGPEN LogPen; } buf = {};
12302 $
int size = Win32::GetObject (obj, 0, NULL);
12303 $ Win32::GetObject (obj,
sizeof (buf), &buf)
asserted;
12305 $
return (size ==
sizeof (LOGPEN))? buf.LogPen.lopnColor : buf.extLogPen.elpColor;
12312 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return NULL;
12314 $ HBRUSH brush = (color ==
TX_TRANSPARENT)? (HBRUSH) Win32::GetStockObject (HOLLOW_BRUSH) : Win32::CreateSolidBrush (color);
12316 $
return (_txBuffer_Select (brush, dc))? brush : (Win32::DeleteObject (brush), (HBRUSH) NULL);
12321 COLORREF txFillColor (
double red,
double green,
double blue)
12323 $1
if (red > 1) red = 1;
if (red < 0) red = 0;
12324 $
if (green > 1) green = 1;
if (green < 0) green = 0;
12325 $
if (blue > 1) blue = 1;
if (blue < 0) blue = 0;
12327 $ COLORREF color = RGB (
ROUND (red * 255),
ROUND (green * 255),
ROUND (blue * 255));
12336 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return CLR_INVALID;
12338 $ HGDIOBJ obj =
txGDI ((Win32::GetCurrentObject (dc, OBJ_BRUSH)), dc);
12339 $
assert (obj);
if (!obj)
return CLR_INVALID;
12341 $ LOGBRUSH buf = {};
12342 $
txGDI ((Win32::GetObject (obj,
sizeof (buf), &buf)), dc)
asserted;
12344 $
return buf.lbColor;
12351 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12354 $
return txGDI (!!(Win32::PatBlt (dc, 0, 0, size.x, size.y, PATCOPY)), dc);
12359 inline bool txSetPixel (
double x,
double y, COLORREF color, HDC dc )
12361 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12370 inline bool txPixel (
double x,
double y,
double red,
double green,
double blue, HDC dc )
12372 $1
if (red > 1) red = 1;
if (red < 0) red = 0;
12373 $
if (green > 1) green = 1;
if (green < 0) green = 0;
12374 $
if (blue > 1) blue = 1;
if (blue < 0) blue = 0;
12381 inline COLORREF
txGetPixel (
double x,
double y, HDC dc )
12383 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return CLR_INVALID;
12390 bool txLine (
double x0,
double y0,
double x1,
double y1, HDC dc )
12392 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12394 $
bool ok =
txGDI ((Win32::MoveToEx (dc,
ROUND (x0),
ROUND (y0), NULL)), dc);
12402 bool txRectangle (
double x0,
double y0,
double x1,
double y1, HDC dc )
12404 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12411 bool txPolygon (
const POINT points[],
int numPoints, HDC dc )
12413 $1
if (_TX_ARGUMENT_FAILED (points))
return false;
12414 $
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12416 $
return txGDI (!!(Win32::Polygon (dc, points, numPoints)), dc);
12421 bool txEllipse (
double x0,
double y0,
double x1,
double y1, HDC dc )
12423 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12430 bool txCircle (
double x,
double y,
double r)
12432 $1
return txEllipse (x-r, y-r, x+r, y+r);
12437 bool txArc (
double x0,
double y0,
double x1,
double y1,
double startAngle,
double totalAngle, HDC dc )
12439 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12441 $ POINT center = {
ROUND ((x0 + x1) /2),
ROUND ((y0 + y1) /2) };
12443 $
double start = startAngle *
txPI/180,
12444 end = (startAngle + totalAngle) *
txPI/180;
12447 ROUND (center.x + 1E3*cos (start)),
ROUND (center.y - 1E3*sin (start)),
12448 ROUND (center.x + 1E3*cos (end)),
ROUND (center.y - 1E3*sin (end)))), dc);
12453 bool txPie (
double x0,
double y0,
double x1,
double y1,
double startAngle,
double totalAngle, HDC dc )
12455 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12457 $ POINT center = {
ROUND ((x0 + x1) /2),
ROUND ((y0 + y1) /2) };
12459 $
double start = startAngle *
txPI/180,
12460 end = (startAngle + totalAngle) *
txPI/180;
12463 ROUND (center.x + 1E3*cos (start)),
ROUND (center.y - 1E3*sin (start)),
12464 ROUND (center.x + 1E3*cos (end)),
ROUND (center.y - 1E3*sin (end)))), dc);
12469 bool txChord (
double x0,
double y0,
double x1,
double y1,
double startAngle,
double totalAngle, HDC dc )
12471 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12473 $ POINT center = {
ROUND ((x0 + x1) /2),
ROUND ((y0 + y1) /2) };
12475 $
double start = startAngle *
txPI/180,
12476 end = (startAngle + totalAngle) *
txPI/180;
12479 ROUND (center.x + 1E3*cos (start)),
ROUND (center.y - 1E3*sin (start)),
12480 ROUND (center.x + 1E3*cos (end)),
ROUND (center.y - 1E3*sin (end)))), dc);
12486 COLORREF color , DWORD mode , HDC dc )
12488 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12492 $
return txGDI (!!(Win32::ExtFloodFill (dc,
ROUND (x),
ROUND (y), color, mode)), dc);
12497 bool txTextOut (
double x,
double y,
const char text[], HDC dc )
12499 $1
if (_TX_ARGUMENT_FAILED (text))
return false;
12500 $
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12502 $
int len = (int) strlen (text);
12503 $
bool ok =
txGDI (!!(Win32::TextOut (dc,
ROUND (x),
ROUND (y), text, len)), dc);
12510 bool txDrawText (
double x0,
double y0,
double x1,
double y1,
const char text[],
12514 $1
if (_TX_ARGUMENT_FAILED (text))
return false;
12515 $
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12517 #if !defined (NDEBUG)
12521 $ SetLastError (ERROR_INVALID_DATA);
12522 $
TX_ERROR (
"Параметр x0 = %lg больше, чем x1 = %lg. Текст выводиться не будет.", x0, x1);
12527 $ SetLastError (ERROR_INVALID_DATA);
12528 $
TX_ERROR (
"Параметр y0 = %lg больше, чем y1 = %lg. Текст выводиться не будет.", y0, y1);
12535 $
if (!strchr (text,
'\n')) format |= DT_SINGLELINE;
12537 $
unsigned prev =
txSetTextAlign (TA_LEFT | TA_TOP | TA_NOUPDATECP, dc);
12541 $
if (Win32::DrawText)
12543 $ ok = !!
txGDI ((Win32::DrawText (dc, text, -1, &r, format)), dc);
12544 $ Win32::GetPixel (dc, 0, 0);
12549 $
txTextOut ((x0 + x1) / 2, (y0 + y1) / 2, text);
12560 HFONT
txSelectFont (
const char name[],
double sizeY,
double sizeX ,
12561 int bold ,
bool italic ,
bool underline ,
12562 bool strikeout ,
double angle ,
12565 $1
if (_TX_ARGUMENT_FAILED (name))
return NULL;
12566 $
if (_TX_DEFAULT_HDC_FAILED (dc))
return NULL;
12569 Win32::CreateFont (
ROUND (sizeY),
ROUND ((sizeX >= 0)? sizeX : sizeY/3),
12570 ROUND (angle*10), 0, bold, italic, underline, strikeout,
12571 RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
12572 DEFAULT_QUALITY, DEFAULT_PITCH, name)
12574 (HFONT) Win32::GetStockObject (SYSTEM_FIXED_FONT);
12576 $ _txBuffer_Select (font, dc);
12585 $1 SIZE size = {-1, -1};
12587 $
if (_TX_ARGUMENT_FAILED (text))
return size;
12588 $
if (_TX_DEFAULT_HDC_FAILED (dc))
return size;
12590 $
size_t len = strlen (text);
12591 $
txGDI ((Win32::GetTextExtentPoint32 (dc, text, (
int) len, &size)), dc)
asserted;
12614 $1
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12616 $
return txGDI ((Win32::SetTextAlign (dc, align)), dc);
12623 $1
if (_TX_ARGUMENT_FAILED (name))
return NULL;
12625 $
static LOGFONT font = {};
12626 $ font.lfCharSet = DEFAULT_CHARSET;
12627 $
strncpy_s (font.lfFaceName, sizeof (font.lfFaceName), name, sizeof (font.lfFaceName) - 1);
12631 static int CALLBACK enumFonts (
const LOGFONT* fnt,
const TEXTMETRIC*, DWORD, LPARAM data)
12633 $
if (_TX_ARGUMENT_FAILED (fnt))
return 0;
12634 $
if (_TX_ARGUMENT_FAILED (data))
return 0;
12636 #ifndef __STRICT_ANSI__
12637 $
return _strnicmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
12640 $
return strncmp (fnt->lfFaceName, ((LOGFONT*)data)->lfFaceName, LF_FACESIZE);
12646 $
return txGDI ((Win32::EnumFontFamiliesEx (NULL, &font, tools::enumFonts, (LPARAM) &font, 0)), NULL) == 0? &font : NULL;
12653 $1
if (_TX_ARGUMENT_FAILED (obj))
return false;
12654 $
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
12656 $
return _txBuffer_Select (obj, dc);
12663 $1 POINT size = {
ROUND (sizeX),
ROUND (sizeY) };
12665 $ HDC dc = _txBuffer_Create (NULL, &size, bitmap);
12666 $
assert (dc);
if (!dc)
return NULL;
12670 $
if (!_txCanvas_UserDCs)
return dc;
12673 $ _txCanvas_UserDCs->push_back (dc);
12676 {$ txNotifyIcon (NIIF_WARNING, NULL,
"Вы загрузили уже %d HDC, системе может стать плохо.", (
int) _txCanvas_UserDCs->size()); }
12685 $1 RGBQUAD* buf = NULL;
12686 $
if (!pixels) pixels = &buf;
12688 $ BITMAPINFO info = {{
sizeof (info),
ROUND (sizeX),
ROUND (sizeY), 1, WORD (
sizeof (RGBQUAD) * 8), BI_RGB }};
12690 $ HDC dc =
txCreateCompatibleDC (0, 0, Win32::CreateDIBSection (NULL, &info, DIB_RGB_COLORS, (
void**) pixels, NULL, 0));
12692 $ RGBQUAD black = { 0, 0, 0, 255 };
12693 $
for (
int i = 0; i < sizeX * sizeY; i++) (*pixels)[i] = black;
12707 HDC
txLoadImage (
const char filename[],
unsigned imageFlags ,
unsigned loadFlags )
12709 $1
if (_TX_ARGUMENT_FAILED (filename && *filename))
return NULL;
12711 $ HBITMAP image = (HBITMAP) Win32::LoadImage ((loadFlags & LR_LOADFROMFILE)? NULL : GetModuleHandle (NULL),
12712 filename, imageFlags, 0, 0, loadFlags);
12713 $
if (!image)
return NULL;
12717 $
if (!(loadFlags & LR_LOADFROMFILE))
return dc;
12719 $
static std::map <std::string, unsigned> loadTimes;
12720 $ std::string file = filename;
12721 $
unsigned time = GetTickCount();
12723 $
if ((
long) (time - loadTimes [file]) <
_TX_TIMEOUT)
12724 {$ txNotifyIcon (NIIF_WARNING, NULL,
"Вы загружаете \"%s\" слишком часто, программа будет тормозить.", filename); }
12726 $ loadTimes [file] = time;
12735 $1
if (_TX_ARGUMENT_FAILED (pdc))
return false;
12738 $
bool ok = _txBuffer_Delete (pdc);
12739 $
if (!ok)
return false;
12741 $
if (!_txCanvas_UserDCs)
return ok;
12744 $ std::vector <HDC> ::iterator it = std::find (_txCanvas_UserDCs->begin(), _txCanvas_UserDCs->end(), dc);
12745 $
if (it != _txCanvas_UserDCs->end()) { std::swap (*it, _txCanvas_UserDCs->back()); _txCanvas_UserDCs->pop_back(); }
12759 bool txBitBlt (HDC destImage,
double xDest,
double yDest,
double width,
double height,
12760 HDC sourceImage,
double xSource ,
double ySource ,
unsigned operation )
12762 $1
if (_TX_HDC_FAILED (destImage))
return false;
12763 $
if (_TX_HDC_FAILED (sourceImage))
return false;
12766 $
if (!width) width = size.x;
12767 $
if (!height) height = size.y;
12770 sourceImage,
ROUND (xSource),
ROUND (ySource), operation)), destImage);
12775 inline bool txBitBlt (
double xDest,
double yDest, HDC sourceImage,
double xSource ,
double ySource )
12777 $1
if (_TX_TXWINDOW_FAILED())
return false;
12779 $
return txBitBlt (
txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource);
12784 bool txTransparentBlt (HDC destImage,
double xDest,
double yDest,
double width,
double height,
12785 HDC sourceImage,
double xSource ,
double ySource , COLORREF transColor )
12787 $1
if (_TX_HDC_FAILED (destImage))
return false;
12788 $
if (_TX_HDC_FAILED (sourceImage))
return false;
12791 $
if (!width) width = size.x;
12792 $
if (!height) height = size.y;
12794 #if !defined (NDEBUG)
12796 $
if (!(0 <= xSource && xSource + width <= size.x &&
12797 0 <= ySource && ySource + height <= size.y))
12799 $ SetLastError (ERROR_INVALID_DATA);
12800 $
TX_ERROR (
"Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
12801 "функция txTransparentBlt() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (
int) size.x, (
int) size.y);
12806 $
bool ok = (Win32::TransparentBlt != NULL);
12817 sourceImage,
ROUND (xSource),
ROUND (ySource), SRCCOPY)),
12826 inline bool txTransparentBlt (
double xDest,
double yDest, HDC sourceImage,
12827 COLORREF transColor ,
double xSource ,
double ySource )
12829 $1
if (_TX_TXWINDOW_FAILED())
return false;
12831 $
return txTransparentBlt (
txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, transColor);
12836 bool txAlphaBlend (HDC destImage,
double xDest,
double yDest,
double width,
double height,
12837 HDC sourceImage,
double xSource ,
double ySource ,
double alpha )
12842 $1
if (_TX_HDC_FAILED (destImage))
return false;
12843 $
if (_TX_HDC_FAILED (sourceImage))
return false;
12848 $
if (!width) width = size.x;
12849 $
if (!height) height = size.y;
12855 #if !defined (NDEBUG)
12857 $
if (!(0 <= xSource && xSource + width <= size.x &&
12858 0 <= ySource && ySource + height <= size.y))
12860 $ SetLastError (ERROR_INVALID_DATA);
12861 $
TX_ERROR (
"Прямоугольник копируемой области {%lg, %lg, %lg, %lg} не полностью лежит внутри изображения-источника {%d, %d, %d, %d}, "
12862 "функция txAlphaBlend() работать не будет.", xSource, ySource, xSource + width, ySource + height, 0, 0, (
int) size.x, (
int) size.y);
12869 $
if (alpha < 0) alpha = 0;
12870 $
if (alpha > 1) alpha = 1;
12874 $ BITMAP bmap = { 0, 0, 0, 0, 0, 24 };
12875 $
bool ok = !!Win32::GetObject (
txGDI ((Win32::GetCurrentObject (sourceImage, OBJ_BITMAP)), sourceImage),
sizeof (bmap), &bmap);
12909 $ BLENDFUNCTION blend = { AC_SRC_OVER, 0, (BYTE)
ROUND (alpha * 255), (BYTE) ((bmap.bmBitsPixel == 32)? AC_SRC_ALPHA : 0) };
12923 $
if (Win32::AlphaBlend)
12934 sourceImage,
ROUND (xSource),
ROUND (ySource), SRCCOPY)),
12951 inline bool txAlphaBlend (
double xDest,
double yDest, HDC sourceImage,
12952 double xSource ,
double ySource ,
double alpha )
12954 $1
if (_TX_TXWINDOW_FAILED())
return false;
12956 $
return txAlphaBlend (
txDC(), xDest, yDest, 0, 0, sourceImage, xSource, ySource, alpha);
12963 $1
if (_TX_HDC_FAILED (image))
return NULL;
12965 $ HBITMAP bitmap = (HBITMAP) Win32::GetCurrentObject (image, OBJ_BITMAP);
12966 $
if (!bitmap)
return NULL;
12968 $ DIBSECTION dib = {};
12969 $ Win32::GetObject (bitmap,
sizeof (dib), &dib)
asserted;
12971 $ POINT size = { dib.dsBm.bmWidth, dib.dsBm.bmHeight };
12972 $ BITMAPINFO info = {{
sizeof (info), size.x, size.y, 1, (WORD) (
sizeof (RGBQUAD) * 8), BI_RGB }};
12973 $ RGBQUAD* buf = NULL;
12975 $
bool isDIB = (dib.dsBm.bmPlanes == 1 &&
12976 dib.dsBm.bmBitsPixel ==
sizeof (RGBQUAD) * 8 &&
12977 dib.dsBmih.biCompression == DIB_RGB_COLORS &&
12981 $ buf =
new (std::nothrow) RGBQUAD [size.x * size.y];
12982 $ if (!buf)
return NULL;
12984 $ Win32::GetDIBits (image, bitmap, 0, size.y, buf, &info, DIB_RGB_COLORS)
asserted;
12988 $ buf = (RGBQUAD*) dib.dsBm.bmBits;
12991 $
for (
int y = 0; y < size.y; y++)
12992 for (
int x = 0; x < size.x; x++)
12994 RGBQUAD* color = &buf [x + y * size.x];
12996 color->rgbRed = (BYTE)
ROUND (color->rgbRed * color->rgbReserved / 255.0);
12997 color->rgbGreen = (BYTE)
ROUND (color->rgbGreen * color->rgbReserved / 255.0);
12998 color->rgbBlue = (BYTE)
ROUND (color->rgbBlue * color->rgbReserved / 255.0);
13003 $ Win32::SetDIBitsToDevice (image, 0, 0, size.x, size.y, 0, 0, 0, size.y, buf, &info, DIB_RGB_COLORS)
asserted;
13013 bool txSaveImage (
const char filename[], HDC dc )
13015 $1
if (_TX_ARGUMENT_FAILED (filename))
return false;
13016 $
if (_TX_DEFAULT_HDC_FAILED (dc))
return false;
13020 $
size_t szHdrs =
sizeof (BITMAPFILEHEADER) +
sizeof (BITMAPINFOHEADER),
13021 szImg = (size.x * size.y) *
sizeof (RGBQUAD);
13023 $ BITMAPFILEHEADER hdr = { 0x4D42 , (DWORD) (szHdrs + szImg), 0, 0, (DWORD) szHdrs };
13024 $ BITMAPINFOHEADER info = {
sizeof (info), size.x, size.y, 1, (WORD) (
sizeof (RGBQUAD) * 8), BI_RGB };
13028 $ RGBQUAD* buf =
new (std::nothrow) RGBQUAD [size.x * size.y];
13029 $ ok &= (buf != NULL);
13031 $
if (ok) Win32::GetDIBits (dc, (HBITMAP) Win32::GetCurrentObject (dc, OBJ_BITMAP), 0, size.y,
13032 buf, (BITMAPINFO*) &info, DIB_RGB_COLORS)
asserted;
13034 $
if (ok)
fopen_s (&f, filename,
"wb");
13035 $ ok &= (f != NULL);
13037 $
if (ok) ok &= (fwrite (&hdr,
sizeof (hdr), 1, f) == 1);
13038 $
if (ok) ok &= (fwrite (&info,
sizeof (info), 1, f) == 1);
13039 $
if (ok) ok &= (fwrite (buf, szImg, 1, f) == 1);
13041 $ ok &= (f && fclose (f) == 0);
13060 $1
return _txCanvas_SetRefreshLock (update >= 0? !update : -update);
13067 $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock + 1);
13069 $
return _txCanvas_RefreshLock;
13076 $1 _txCanvas_SetRefreshLock (_txCanvas_RefreshLock - 1);
13078 $
return _txCanvas_RefreshLock;
13085 $1 LARGE_INTEGER start = {};
13086 $ QueryPerformanceCounter (&start)
asserted;
13088 $ LARGE_INTEGER freq = {};
13089 $ QueryPerformanceFrequency (&freq)
asserted;
13091 $
int lock = _txCanvas_RefreshLock;
13092 $ _txCanvas_RefreshLock = 0;
13095 if (wnd) {$ RedrawWindow (wnd, NULL, NULL, RDW_INVALIDATE | RDW_INTERNALPAINT | RDW_UPDATENOW); }
13097 $ Sleep (
ROUND ((time >= 0)? time : 0));
13099 $ _txCanvas_RefreshLock = lock;
13101 $ LARGE_INTEGER stop = {};
13102 $ QueryPerformanceCounter (&stop)
asserted;
13104 $
return 1000.0 * (double) (stop.QuadPart - start.QuadPart) / (double) freq.QuadPart;
13109 bool txLock (
bool wait )
13111 $0
if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
13113 $
if (wait) {$
return EnterCriticalSection (&_txCanvas_LockBackBuf),
true; }
13114 else {$
return !!TryEnterCriticalSection (&_txCanvas_LockBackBuf); }
13121 $0 LeaveCriticalSection (&_txCanvas_LockBackBuf);
13123 $
if (_txCanvas_RefreshLock <= 0 || _txExit) Sleep (0);
13129 template <
typename T>
13141 $ GetCursorPos (&pos);
13144 {$ ScreenToClient (
txWindow(), &pos); }
13167 $1
return ((GetAsyncKeyState (VK_LBUTTON) & 0x8000) >> 15) |
13168 ((GetAsyncKeyState (VK_RBUTTON) & 0x8000) >> 14) |
13169 ((GetAsyncKeyState (VK_MBUTTON) & 0x8000) >> 13);
13178 SetConsoleTextAttribute (GetStdHandle (STD_OUTPUT_HANDLE), (WORD) color);
13187 CONSOLE_SCREEN_BUFFER_INFO con = {};
13188 GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con);
13190 return con.wAttributes;
13199 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
13200 $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con)
asserted;
13202 $ COORD pos = { (short)
ROUND (1.0 * x / fontSz.x + con.srWindow.Left),
13203 (short)
ROUND (1.0 * y / fontSz.y + con.srWindow.Top ) };
13205 $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), pos)
asserted;
13207 $ POINT prev = {
ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
13208 ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
13218 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
13219 $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con)
asserted;
13221 $ POINT pos = {
ROUND ((con.dwCursorPosition.X - con.srWindow.Left) * fontSz.x),
13222 ROUND ((con.dwCursorPosition.Y - con.srWindow.Top ) * fontSz.y) };
13230 $1 CONSOLE_SCREEN_BUFFER_INFO con = {};
13231 $ GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &con)
asserted;
13233 $ POINT size = { con.srWindow.Right - con.srWindow.Left + 1,
13234 con.srWindow.Bottom - con.srWindow.Top + 1 };
13242 $1 HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
13244 $ CONSOLE_SCREEN_BUFFER_INFO con = {};
13245 $ GetConsoleScreenBufferInfo (out, &con)
asserted;
13247 $ COORD start = {con.srWindow.Left, con.srWindow.Top};
13249 $ DWORD len = (con.srWindow.Right - con.srWindow.Left + 1) *
13250 (con.srWindow.Bottom - con.srWindow.Top + 1);
13252 $ DWORD written = 0;
13253 $ FillConsoleOutputCharacter (out, 0x20 , len, start, &written)
asserted;
13254 $ FillConsoleOutputAttribute (out, con.wAttributes, len, start, &written)
asserted;
13256 $ SetConsoleCursorPosition (GetStdHandle (STD_OUTPUT_HANDLE), start)
asserted;
13258 $
return written == len;
13265 $1 Win32::CONSOLE_FONT_INFO font = {0, {8, 16}};
13267 $ _TX_CALL (Win32::GetCurrentConsoleFont, (GetStdHandle (STD_OUTPUT_HANDLE),
false, &font));
13269 $ SIZE size = { font.dwFontSize.X, font.dwFontSize.Y };
13270 $
txGDI (Win32::GetTextExtentPoint32 (_txCanvas_BackBuf[1],
"W", 1, &size),
txDC());
13272 $ POINT sizeFont = { size.cx, size.cy };
13280 $1
bool old = _txConsole_IsBlinking;
13282 $ _txConsole_IsBlinking = blink;
13289 bool txPlaySound (
const char filename[] , DWORD mode )
13291 $1 mode |= SND_FILENAME | SND_NODEFAULT | SND_NOWAIT;
13292 $
if (mode & SND_LOOP) mode = (mode & ~SND_SYNC) | SND_ASYNC;
13294 $
if (!filename) mode = SND_PURGE;
13296 $
return !!Win32::PlaySound (filename, NULL, mode);
13301 int txSpeak (
const char* text, ...)
13303 $1
bool verbose =
false; (void) verbose;
13304 $
bool async =
false; (void) async;
13306 $
for (; text && *text; text++)
13308 if (*text ==
'\a') {$ async =
true; }
13309 else if (*text ==
'\v') {$ verbose =
true; }
13313 $
char textA [
_TX_BUFSIZE] =
"You asked to speak empty text. I would rather say that TX Library is cool! Cats rules!";
13315 $ va_list arg; va_start (arg, text);
13316 if (text && *text) {$ _tx_vsnprintf_s (textA,
sizeof (textA) - 1, text, arg); }
13319 #ifdef TX_USE_SPEAK
13321 if (text && verbose) {$ printf (
"%s", textA); }
13323 $
int time = GetTickCount();
13325 $
static wchar_t textW [
_TX_BUFSIZE *
sizeof (wchar_t)] = L
"";
13328 $
static ISpVoice* voice = NULL;
13330 $
if (text && !voice)
13332 $ HRESULT res = Win32::CoInitialize (NULL);
13333 if (res == S_OK) {$ Win32::CoCreateInstance (Win32::CLSID_SpVoice, NULL, CLSCTX_ALL, Win32::IID_ISpVoice, (
void**) &voice); }
13336 $
if (text && voice)
13338 $ Win32::_fpreset();
13339 $ voice->Speak (textW, SPF_PERSIST_XML | SPF_PURGEBEFORESPEAK | (async? SPF_ASYNC : 0), NULL);
13343 $
if (!text && voice)
13345 $ voice->Release();
13348 $ Win32::CoUninitialize();
13351 $
return (voice)? GetTickCount() - time : -1;
13357 $
unsigned oldAttr =
txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK);
13359 $ txNotifyIcon (NIIF_ERROR,
"txSpeak(): Не могу произнести (нужен TX_USE_SPEAK, см. TXLib Help)",
"\n" "%s", textA);
13371 intptr_t
txPlayVideo (
int x,
int y,
int width,
int height,
const char fileName[],
13372 double zoom ,
double gain , HWND wnd )
13374 $1
if (wnd && wnd ==
txWindow() && _TX_TXWINDOW_FAILED())
return -1;
13376 $
int time = GetTickCount();
13378 $
static char processUID [64] =
"";
13381 $ FILETIME startTime = {},
null = {};
13382 $ GetProcessTimes (GetCurrentProcess(), &startTime, &
null, &
null, &
null)
asserted;
13384 (
unsigned) startTime.dwHighDateTime, (
unsigned) startTime.dwLowDateTime) < (
int) sizeof (processUID)
asserted;
13389 $ txTaskKill (
"vlc.exe", processUID, 0);
13393 $
static const char* vlcPath = _txPlayVideo_FindVLC();
13395 $
if (!vlcPath || _access (vlcPath, 0) != 0)
13397 $
static int once =
false;
13399 $
if (*fileName && !once++)
13400 {$
txOutputDebugPrintf (
"\a" "Не найден видеопроигрыватель VideoLAN (vlc.exe). Cкачайте его с сайта VideoLAN.org "
13401 "и установите. Без установки VideoLAN видео воспроизводиться не будет :(\n\n"
13402 "--\n" "Всегда Ваша, функция " "txPlayVideo()...\n"
13403 "P.S. См. мое описание в TXLib Help."); }
13407 $
bool async =
false;
13408 if (*fileName ==
'\a') {$ async =
true; fileName++; }
13411 if (wnd) {$ GetClientRect (wnd, &rect); }
13413 if (!width) {$ width = rect.right; }
13414 if (!height) {$ height = rect.bottom; }
13418 $
const char* errPos =
"ВНЕЗАПНО";
13420 $
volatile HWND child = NULL;
13423 $
const char* wndClass = txRegisterClass (
"txPlayVideo", _txPlayVideo_WndProc, 0, NULL_BRUSH, 1);
13425 $
static int number = 1;
13426 $ CREATESTRUCT createData = { NULL, NULL, (HMENU) (
size_t) number++,
txWindow(), height, width, y, x,
13427 WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, __func__, wndClass };
13428 $ child = txCreateExtraWindow (createData);
13431 $ txNotifyIcon (NIIF_ERROR,
"txPlayVideo() сообщает",
"\n" "%s",
13432 strstr (_txError (NULL, 0, NULL, 0,
"\f" "Не могу создать окно для видео :("), errPos));
13433 $
return INT_MIN+3;
13436 $ BringWindowToTop (child);
13443 if (!zoom && !wnd) {$ zoom = 1; }
13445 $
char sZoom [64] =
"--autoscale";
13446 if (zoom) {$
_snprintf_s (sZoom,
sizeof (sZoom) - 1,
"--no-autoscale --zoom=%.10g", zoom) < (int)
sizeof (sZoom)
asserted; }
13448 $
static char cmd [MAX_PATH*2 + 1024] =
"";
13450 $
_snprintf_s (cmd,
sizeof (cmd) - 1,
"\"%s\" \"%s\" vlc://quit"
13452 " %s --gain=%.10g --drawable-hwnd=%" PRIu64 " --video-title=\"%s\" --logfile=%s"
13454 " --live-caching=500 --network-caching=500 --quiet-synchro --no-embedded-video --file-logging"
13456 " --ignore-config --reset-config --no-one-instance --play-and-exit"
13457 " --intf=dummy --dummy-quiet --quiet --no-video-deco --no-video-title-show --no-stats --no-sub-autodetect-file"
13458 " --no-disable-screensaver --no-snapshot-preview --no-auto-preparse --no-mouse-events --no-keyboard-events",
13460 vlcPath, (*fileName? fileName :
"fileName"), sZoom, gain, (uint64_t) wnd, processUID,
_txLogName) < (int)
sizeof (cmd)
asserted;
13463 x, y, width, height, fileName, zoom, gain, wnd, cmd);
13467 $
return (intptr_t) cmd;
13470 $
if (!strstr (fileName,
"://") && _access (fileName, 0) != 0)
13472 $ txNotifyIcon (NIIF_ERROR,
"txPlayVideo() сообщает",
"\n" "%s",
13473 strstr (_txError (NULL, 0, NULL, 0,
"\f" "Не найден файл \"%s\"", fileName), errPos));
13476 $
return INT_MIN+1;
13481 $ PROCESS_INFORMATION vlc = {};
13482 $ STARTUPINFO start = {
sizeof (start) };
13485 $
if (CreateProcess (NULL, cmd, NULL, NULL,
true, 0, NULL, NULL, &start, &vlc) &&
13486 vlc.hProcess && vlc.hThread)
13490 $
assert (wnd == child);
13491 $ SetWindowLongPtr (wnd, GWLP_USERDATA, (LONG_PTR) vlc.hProcess);
13496 $ WaitForSingleObject (vlc.hProcess, INFINITE);
13497 $ GetExitCodeProcess (vlc.hProcess, &ret)
asserted;
13502 $ CloseHandle (vlc.hProcess)
asserted;
13505 $ CloseHandle (vlc.hThread)
asserted;
13507 $ return (async? (intptr_t) wnd : (ret == 0)? time - GetTickCount() : - (
int) ret);
13511 $ txNotifyIcon (NIIF_ERROR,
"txPlayVideo() сообщает",
"%s",
13512 strstr (_txError (NULL, 0, NULL, 0,
"\f" "Ошибка запуска VideoLAN (%s)", cmd), errPos));
13516 $
return INT_MIN+4;
13519 #undef PROCESS_UID_
13524 inline intptr_t
txPlayVideo (
const char fileName[],
double zoom ,
double gain , HWND wnd )
13526 $1
return txPlayVideo (0, 0, 0, 0, fileName, zoom, gain, wnd);
13531 LRESULT CALLBACK _txPlayVideo_WndProc (HWND wnd, UINT msg, WPARAM wpar, LPARAM lpar)
13533 const UINT_PTR checkTimer = 1;
13545 $1 KillTimer (wnd, checkTimer)
asserted;
13547 $ HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
13551 $ Win32::TerminateProcess (vlc, 0);
13555 $ SetWindowLongPtr (wnd, GWLP_USERDATA, 0);
13562 HANDLE vlc = (HANDLE)(uintptr_t) GetWindowLongPtr (wnd, GWLP_USERDATA);
13564 if (vlc && WaitForSingleObject (vlc, 0) != WAIT_TIMEOUT)
13575 return DefWindowProc (wnd, msg, wpar, lpar);
13580 const char* _txPlayVideo_FindVLC()
13582 $1
static char vlcPath [MAX_PATH] =
"";
13584 $
if (SearchPath (NULL,
"vlc.bat", NULL,
sizeof (vlcPath), vlcPath, NULL))
13586 if (_access (vlcPath, 0) == 0) {$
return vlcPath; }
13589 $
if (SearchPath (NULL,
"vlc.exe", NULL,
sizeof (vlcPath), vlcPath, NULL))
13591 if (_access (vlcPath, 0) == 0) {$
return vlcPath; }
13594 $
if (
txRegQuery (
"HKLM\\Software\\VideoLAN\\VLC", NULL, vlcPath,
sizeof (vlcPath)))
13596 if (_access (vlcPath, 0) == 0) {$
return vlcPath; }
13599 $
if (
txRegQuery (
"HKLM\\Software\\VideoLAN\\VLC",
"InstallDir", vlcPath,
sizeof (vlcPath)))
13601 $
strncat_s (vlcPath,
sizeof (vlcPath) - 1,
"\\vlc.exe", INT_MAX);
13603 if (_access (vlcPath, 0) == 0) {$
return vlcPath; }
13606 $
strncpy_s (vlcPath,
sizeof (vlcPath),
"C:\\Program Files" "\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
13608 if (_access (vlcPath, 0) == 0) {$
return vlcPath; }
13611 $
strncpy_s (vlcPath,
sizeof (vlcPath),
"C:\\Program Files (x86)\\VideoLAN\\VLC\\vlc.exe", UINT_MAX);
13613 if (_access (vlcPath, 0) == 0) {$
return vlcPath; }
13626 $1 WNDPROC old = _txAltWndProc; _txAltWndProc = wndProc;
13638 txMessageBox (
"Это запланированная ошибка. Такое бывает. Вы хотели вызвать:\n\n"
13640 "txIDontWantToHaveAPauseAfterMyProgramBeforeTheWindowWillClose_AndIWillNotBeAskingWhereIsMyPicture()\n\n"
13642 "Хоть вы долго [копировали]набирали это имя, на самом деле эта функция не реализована. "
13643 "Есть другая функция, которая убирает авто-паузу в конце программы, но в хелпе про нее не написано.\n\n"
13645 "Но не все так плохо. Определение нужной функции есть в исходных текстах TXLib.h, оно лежит рядом "
13646 "с определением той функции с длинным названием, которую вы сейчас вызвали.\n\n"
13648 "Нажмите в редакторе Ctrl+O, найдите и откройте файл TXLib.h (он лежит в папке, куда вы "
13649 "установили TXLib), затем нажмите Ctrl+F и ищите \"txIDontWant\". Удачи!\n\n",
13651 "Не получилось", MB_ICONSTOP);
13662 inline bool txDisableAutoPause()
13672 void _txDump (
const void* address,
const char name[] ,
bool pause )
13674 $1
assert (!_txIsBadReadPtr (address));
13676 const unsigned char* p = (
const unsigned char*) address;
13682 $ printf (
"\n%*.*s ", (
int)
sizeof (address) * 2, (
int)
sizeof (address) * 2, ((name)? name :
""));
13685 $
for (x = 0; x < 16; x++) printf (
"%02X ", x);
13686 $
for (x = 0; x < 16; x++) printf (
"%X", x);
13688 $
const char isCtrl[] =
"\a" "\b" "\n" "\r" "\t" "\0";
13689 $
const char xlatCh[] =
"·" "·" "·" "·" "·" "·";
13690 $
const size_t szCtrl =
sizeof (isCtrl) - 1;
13692 $
int oldCP = GetConsoleOutputCP();
13694 $
for (
int y = 0; y < 16; y++, p += 16)
13698 printf (
"\n" "%*p ", (
int)
sizeof (address) * 2, p);
13700 int color = FOREGROUND_LIGHTGREEN;
13702 for (x = 0; x < 16; x++)
13705 printf (
"%02X ", p[x]);
13708 for (x = 0; x < 16; x++)
13713 const char* ctrl = (
const char*) memchr (isCtrl, c, szCtrl);
13714 if (ctrl) c = xlatCh [ctrl - isCtrl];
13716 if (ctrl) SetConsoleOutputCP (1251);
13720 if (ctrl) SetConsoleOutputCP (oldCP);
13731 $ fprintf (stderr,
"[Нажмите любую клавишу для продолжения] CodePage = %5d\n", oldCP);
13734 $ SetConsoleOutputCP (oldCP);
13740 void _txStackBackTrace (
const char file[] ,
int line ,
const char func[] ,
13746 $ fprintf (stderr,
"\n" "--------------------------------------------------\n"
13747 "Трассировка стека из \"%s\" at %s (%d):\n\n"
13749 "--------------------------------------------------\n\n",
13750 func, file, line, _txCaptureStackBackTrace (1, readSource));
13757 char*
txDemangle (
const char* mangledName, std::nomeow_t)
13759 $1
if (!mangledName)
return NULL;
13761 $
char* typeName = NULL;
13763 #if defined (_GCC_VER)
13766 $ typeName = ::abi::__cxa_demangle (mangledName, 0, 0, &err); (void) err;
13767 if (typeName) {$
return typeName; }
13771 $
unsigned short flags = 0;
13773 $
if (mangledName[0] ==
'.')
13779 $ typeName = _TX_CALL (Win32::__unDName, (NULL, mangledName, 0, malloc, free, flags));
13780 if (typeName) {$
return typeName; }
13782 $
return _strdup (mangledName);
13787 std::string
txDemangle (
const char* mangledName)
13789 $1
char* typeName =
txDemangle (mangledName, std::nomeow);
13790 $ std::string name (typeName? typeName :
"");
13800 $1
int maxTime = 500;
13801 $
int maxSamples = 100;
13802 $ POINT size = {100, 100};
13804 $ HDC dc = _txBuffer_Create (
txWindow(), &size, NULL);
13805 $
assert (dc);
if (!dc)
return -1;
13807 $ DWORD mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), 1);
13810 $ LARGE_INTEGER freq = {};
13811 $ QueryPerformanceFrequency (&freq)
asserted;
13813 $ LARGE_INTEGER start = {};
13814 $ QueryPerformanceCounter (&start)
asserted;
13817 $
while (samples++ < maxSamples)
13819 $ LARGE_INTEGER cur = {};
13820 $ QueryPerformanceCounter (&cur)
asserted;
13822 $
double t = 1000.0 * (double) (cur.QuadPart - start.QuadPart) / (double) freq.QuadPart;
13823 $ if (t > maxTime)
break;
13827 $
for (
int y = 0; y < size.y; y++)
13830 $
for (
int y = 0; y < size.y; y += 10)
13831 for (
int x = 0; x < size.x; x += 50)
txTextOut (x, y,
"*", dc);
13833 $
txEllipse (0, 0, size.x, size.y, dc);
13837 $
txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, 0, size.y/2)
asserted;
13839 $
txBitBlt (dc, size.x/2, size.y/2, size.x/2, size.y/2, dc, size.x/2, 0)
asserted;
13842 $ mask = (DWORD) SetThreadAffinityMask (GetCurrentThread(), mask);
13845 $ _txBuffer_Delete (&dc);
13847 $ return 3.0 * samples / sqrt (1.0 * size.x * size.y);
13852 #if defined (_TX_CPP11)
13853 template <
int txFramesToAverage>
13858 $1
static LARGE_INTEGER time0 = {};
if (!time0.QuadPart) QueryPerformanceCounter (&time0);
13859 $ LARGE_INTEGER time = {}; QueryPerformanceCounter (&time);
13861 $
if (time.QuadPart - time0.QuadPart == 0)
13864 $ LARGE_INTEGER freq = {}; QueryPerformanceFrequency (&freq);
13866 $
double fps = (double) freq.QuadPart / (
double) (time.QuadPart - time0.QuadPart);
13869 $
if (txFramesToAverage == 0)
return fps;
13871 $
static double average [txFramesToAverage] = {};
13872 $
static unsigned n = 0;
13874 $ average [n++ % txFramesToAverage] = fps;
13876 $
unsigned nn =
MIN (n, (
unsigned)
sizearr (average));
13878 $
static double median [txFramesToAverage] = {};
13879 $ std::copy (average, average + nn, median);
13880 $ std::nth_element (median, median + nn/2, median + nn);
13882 $ fps = (median [(nn-1) / 2] + median [nn / 2]) / 2.0;
13884 $
return ((
int)n >=
MIN (minFrames, txFramesToAverage))? fps : 0;
13891 $1
switch (component)
13894 case TX_HUE: $
return (color >> 0) & 0xFF;
13902 default: $
return CLR_INVALID;
13914 $
double m1 =
MAX (
MAX (r, g), b) / 255.0,
13915 m2 =
MIN (
MIN (r, g), b) / 255.0,
13927 $
const double prec = 0.001;
13929 $
if (fabs (dm) < prec)
13931 $ is = dm / ((sm <= 1)? sm : (2-sm));
13933 $
double cr = (m1 - ir) / dm,
13934 cg = (m1 - ig) / dm,
13935 cb = (m1 - ib) / dm;
13937 $
if (fabs (ir - m1) < prec) ih = cb - cg;
13938 $
if (fabs (ig - m1) < prec) ih = 2 + cr - cb;
13939 $
if (fabs (ib - m1) < prec) ih = 4 + cg - cr;
13942 $ ih = (ih >= 0)? ih*60 : ih*60 + 360;
13944 $
return RGB (
ROUND (ih / 360 * 255),
ROUND (is * 255),
ROUND (il * 255));
13953 static double calc (
double h,
double m1,
double m2)
13955 $
if (h < 0) h += 360;
13956 $
if (h > 360) h -= 360;
13958 $
return (h < 60)? m1 + (m2-m1) * h / 60 :
13960 (h < 240)? m1 + (m2-m1) * (240-h) / 60 :
13969 $
double ih = h / 255.0 * 360.0,
13973 m2 = (il <= 0.5)? il * (1 + is) : il + is - il * is,
13976 ir = s? xRGB::calc (ih + 120, m1, m2) : il,
13977 ig = s? xRGB::calc (ih, m1, m2) : il,
13978 ib = s? xRGB::calc (ih - 120, m1, m2) : il;
13985 inline double random (std::nomeow_t,
double left,
double right)
13987 return left + (right - left) * ((
double) rand() / RAND_MAX);
13992 template <
typename Tx,
typename Ta,
typename Tb>
13993 inline bool In (std::nomeow_t, Tx x, Ta a, Tb b)
13995 return a <= x && x <= b;
14000 inline int random (
int range)
14002 if (rand() % 100 == 0) fprintf (stderr,
"%.4s ", (
const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
14004 return rand() % range;
14009 inline double random (
double left,
double right)
14011 if (rand() % 100 == 0) fprintf (stderr,
"%.4s ", (
const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
14013 return random (std::nomeow, left, right);
14018 template <
typename Tx,
typename Ta,
typename Tb>
14019 inline bool In (Tx x, Ta a, Tb b)
14021 if (rand() % 100 == 0) fprintf (stderr,
"%.4s ", (
const volatile char*) ((rand() & 0x0F)? &_txCanaryFirst : &_txCanaryLast));
14023 return In (std::nomeow, x, a, b);
14028 inline bool In (
const POINT& pt,
const RECT& rect)
14030 if (_TX_ARGUMENT_FAILED (&pt))
return false;
14031 if (_TX_ARGUMENT_FAILED (&rect))
return false;
14033 return In (std::nomeow, pt.x, rect.left, rect.right) &&
14034 In (std::nomeow, pt.y, rect.top, rect.bottom);
14039 inline bool In (
const COORD& pt,
const SMALL_RECT& rect)
14041 if (_TX_ARGUMENT_FAILED (&pt))
return false;
14042 if (_TX_ARGUMENT_FAILED (&rect))
return false;
14044 return In (std::nomeow, pt.X, rect.Left, rect.Right) &&
14045 In (std::nomeow, pt.Y, rect.Top, rect.Bottom);
14054 $ Win32::_fpreset();
14056 $
unsigned new87 = 0x0008001C;
14058 #if !defined (__CYGWIN__)
14060 $
unsigned old87 = 0;
14062 {$ (void)
_controlfp_s (&old87, old87 & ~new87, 0x0008001F); }
14066 $ Win32::_controlfp (Win32::_controlfp (0, 0) & ~new87, 0x0008001F);
14073 template <
typename T>
14074 inline T zero() { T __zero = {};
return __zero; }
14084 #if defined (_TX_CPP11)
14086 template <
typename T,
typename... ArgsT>
void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt,
const T& arg, ArgsT... args);
14087 template <
typename T,
typename... ArgsT>
void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt, width_t width,
const T& arg, ArgsT... args);
14088 template <
typename T,
typename... ArgsT>
void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt, precision_t prec,
const T& arg, ArgsT... args);
14089 template <
typename T,
typename... ArgsT>
void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt, width_t width, precision_t prec,
const T& arg, ArgsT... args);
14090 void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt);
14092 template <
typename T>
void _txPrintV (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt,
const T& arg);
14093 void _txPrintV (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt,
int* arg);
14094 void _txPrintV (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt);
14098 template <
typename T,
typename... ArgsT>
14099 void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt,
const T& arg, ArgsT... args)
14103 $ _txPrintV (stream, format, n, fmt);
14105 if (fmt[0] ==
'%') {$}
14106 else {$
TX_ERROR (
"\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt,
txTypename (arg), n, format); }
14108 $ _txPrintV (stream, format, n, fmt, arg);
14110 $ _txPrintF (stream, format, n+1, fmt, args...);
14115 template <
typename T,
typename... ArgsT>
14116 void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt, width_t width,
const T& arg, ArgsT... args)
14121 $ _txPrintV (stream, format, n, fmt);
14123 if (fmt[0] ==
'%' && fmt[1] ==
'*') {$ stream << std::setw (width); }
14124 else {$
TX_ERROR (
"\"%%*\" required to setwidth (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, fmt,
txTypename (arg), n, format); }
14126 $ _txPrintV (stream, format, n, fmt, arg);
14128 $ _txPrintF (stream, format, n+1, fmt, args...);
14133 template <
typename T,
typename... ArgsT>
14134 void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt, precision_t prec,
const T& arg, ArgsT... args)
14139 $ _txPrintV (stream, format, n, fmt);
14141 if (fmt[0] ==
'%' && fmt[1] ==
'.' && fmt[2] ==
'*') {$ stream << std::setprecision (prec+1); }
14142 else {$
TX_ERROR (
"\"%%.*\" required to setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", prec, fmt,
txTypename (arg), n, format); }
14144 $ _txPrintV (stream, format, n, fmt, arg);
14146 $ _txPrintF (stream, format, n+1, fmt, args...);
14151 template <
typename T,
typename... ArgsT>
14152 void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt, width_t width, precision_t prec,
const T& arg, ArgsT... args)
14157 $ _txPrintV (stream, format, n, fmt);
14159 if (fmt[0] ==
'%' && fmt[1] ==
'*' && fmt[2] ==
'.' && fmt[3] ==
'*') {$ stream << std::setw (width) << std::setprecision (prec+1); }
14160 else {$
TX_ERROR (
"\"%%*.*\" required to setwidth (%d) and setprecision (%d) in ...\"%s\" while printing %s argument %d in \"%s\"", width, prec, fmt,
txTypename (arg), n, format); }
14162 $ _txPrintV (stream, format, n, fmt, arg);
14164 $ _txPrintF (stream, format, n+1, fmt, args...);
14169 void _txPrintF (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt)
14173 $ _txPrintV (stream, format, n, fmt);
14176 else {$
TX_ERROR (
"No argument provided for %% in \"%s\" while printing \"%s\"", fmt, format); }
14181 void _txPrintV (std::ostringstream& stream,
const char*,
int,
const char*& fmt)
14190 if (fmt[1] ==
'%') fmt++;
14200 template <
typename T>
14201 void _txPrintV (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt,
const T& arg)
14206 $
if (_TX_ARGUMENT_FAILED (&arg))
return;
14208 if (fmt[0] ==
'%') {$}
14209 else {$
TX_ERROR (
"\"%%$\" required to print an argument in ...\"%s\" while printing %s argument %d in \"%s\"", fmt,
txTypename (arg), n, format); }
14213 $
char oldFill = stream.fill (
' ');
14214 $ std::ios_base::fmtflags oldFlags = stream.flags();
14216 $
for (;;)
switch (*fmt)
14218 case '-': $ stream << std::left; fmt++;
break;
14219 case '+': $ stream << std::showpos; fmt++;
break;
14220 case ' ': $ stream.fill (
' '); fmt++;
break;
14221 case '#': $ stream << std::showbase; fmt++;
break;
14222 case '0': $ stream.fill (
'0'); fmt++;
break;
14224 default: $
goto end;
14228 $
int width = (*fmt !=
'*')? (
int) strtoul (fmt,
const_cast <char**
> (&fmt), 10) : (fmt++, 0);
14229 $
int prec = (*fmt ==
'.')? (*++fmt !=
'*')? (int) strtoul (fmt,
const_cast <char**
> (&fmt), 10) : (fmt++, 0) : 0;
14231 if (width) {$ stream << std::setw (width); }
14232 if (prec) {$ stream << std::setprecision (prec); }
14234 $ fmt += strspn (fmt,
"hljztL");
14243 case 'u': $ stream << std::dec;
break;
14245 case 'o': $ stream << std::oct;
break;
14247 case 'x': $ stream << std::hex;
break;
14248 case 'X': $ stream << std::hex << std::uppercase;
break;
14250 case 'f': $ stream << std::fixed;
break;
14251 case 'F': $ stream << std::fixed << std::uppercase;
break;
14253 case 'e': $ stream << std::scientific;
break;
14254 case 'E': $ stream << std::scientific << std::uppercase;
break;
14257 case 'G': $ stream << std::uppercase;
break;
14260 case 'A': $ stream << std::uppercase;
break;
14266 default: $
TX_ERROR (
"Invalid format '%.1s' at \"%s\" while printing %s argument %d in \"%s\"", fmt, fmt,
txTypename (arg), n, format);
break;
14271 if (&arg) {$ stream << arg; }
14272 else {$ stream <<
"(null)"; }
14274 $ stream.fill (oldFill);
14275 $ stream.flags (oldFlags);
14280 void _txPrintV (std::ostringstream& stream,
const char* format,
int n,
const char*& fmt,
int* arg)
14284 if (_TX_ARGUMENT_FAILED (arg))
return;
14286 if (fmt[0] ==
'%' && fmt[1] ==
'n') {$}
14287 else {$
TX_ERROR (
"\"%%n\" required to store print length in int* argument %d in \"%s\"", n, format); }
14289 $ *arg = (int) stream.str().length();
14296 template <
typename T>
inline const T& _txPrintfNormalizeArg (
const T& arg) {
if (_TX_ARGUMENT_FAILED (&arg)) {;}
return arg; }
14297 inline const char* _txPrintfNormalizeArg (
const std::string& arg) {
if (_TX_ARGUMENT_FAILED (&arg))
return NULL;
return arg.c_str(); }
14301 template <
typename... ArgsT>
14302 inline int txPrintf (std::ostringstream& stream,
const char* format, ArgsT... args)
14304 $1
if (_TX_ARGUMENT_FAILED (&stream))
return 0;
14305 $
if (_TX_ARGUMENT_FAILED (&format))
return 0;
14307 $
const char* fmt = format;
14308 $ _txPrintF (stream, format, 2, fmt, _txPrintfNormalizeArg (args)...);
14310 $
return (
int) stream.str().length();
14315 template <
typename... ArgsT>
14316 inline int txPrintf (
char buffer[],
size_t size,
const char* format, ArgsT... args)
14318 $1
if (_TX_ARGUMENT_FAILED (&buffer))
return 0;
14319 $
if (_TX_ARGUMENT_FAILED (&format))
return 0;
14321 $
if (size > 0) size--;
14322 $ buffer[size] = 0;
14324 $
if (!size)
return 0;
14326 $ std::ostringstream stream;
14327 $ stream.rdbuf() -> pubsetbuf (buffer, size);
14329 $ txPrintf (stream, format, args...);
14331 $
return (
int) stream.str().length();
14336 template <
typename... ArgsT>
14337 inline std::string txFormat (
const char* format, ArgsT... args)
14339 $1
if (_TX_ARGUMENT_FAILED (&format))
return "";
14341 $ std::ostringstream stream;
14343 $ txPrintf (stream, format, args...);
14345 $
return stream.str();
14350 template <
typename... ArgsT>
14351 inline int txPrintf (
const char* format, ArgsT... args)
14353 $1
if (_TX_ARGUMENT_FAILED (&format))
return 0;
14355 $
return printf (
"%s", txFormat (format, args...) .c_str());
14362 int _txPrintfCheck (
const char* format, ...)
tx_printfy (1);
14363 inline
int _txPrintfCheck (const
char*, ...) {
return 0; }
14393 $ return ::std::swap (layout_, layout), layout;
14400 $1
const char* resName = (
char*)(uintptr_t) resourceID;
14402 $
if (!FindResource (NULL, resName, RT_DIALOG))
return TX_DEBUG_ERROR (
"Не найден ресурс диалога %d", resourceID), 0;
14404 $
return DialogBoxParam (NULL, resName, NULL, (DLGPROC)
DialogProc_, (LPARAM)
this);
14411 $1
if (!layout) layout = layout_;
14412 $
if (!layout)
return TX_DEBUG_ERROR (
"Не установлен динамический шаблон диалога ), 0;
$ if (!bufsize) bufsize = 1024;
$ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize);
$ if (!tmpl) return TX_DEBUG_ERROR ("GlobalAlloc(): Нет памяти для шаблона диалога"), 0;
$ const Layout* dlg = &layout[0];
$ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 };
$ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize,
(dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0,
dlg->x, dlg->y, dlg->sx, dlg->sy,
dlg->caption? dlg->caption : def.caption,
dlg->font? dlg->font : def.font,
dlg->fontsize? dlg->fontsize : def.fontsize, NULL);
$ WORD i = 0;
$ for (i = 1; layout[i].wndclass != END; ++i)
{
$ const Layout* item = &layout[i];
$ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl),
item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy,
item->id, (const char*)(uintptr_t) item->wndclass, item->caption);
}
$ tmpl->cdit = (unsigned short) (i-1);
$ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
$ GlobalFree (tmpl);
$ return res;
}
//-----------------------------------------------------------------------------------------------------------------
int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM)
{
$1 switch (msg)
{
case WM_INITDIALOG: $ SetForegroundWindow (wnd);
$ break;
case WM_COMMAND: $ switch (LOWORD (wParam))
{
case IDOK:
case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow());
$ EndDialog (wnd, (uintptr_t) this);
$ break;
default: $ break;
}
$ break;
default: $ break;
}
$ return FALSE;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
$1 static txDialog* this__ = NULL;
$ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam;
$ if (!this__) return FALSE;
$ return this__-> dialogProc (wnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle,
WORD controls, short x, short y, short cx, short cy,
const char caption[], const char font[], WORD fontsize, const char menu[])
{
$1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL;
$ WORD* pw = (WORD*) globalMem;
$ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->cdit = controls;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ if (menu > (const char*) 0xFFFF)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
else
{
$ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0);
$ *pw++ = (WORD)(uintptr_t) menu;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ if (style & DS_SETFONT)
{
$ *pw++ = fontsize;
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ return pw;
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle,
short x, short y, short cx, short cy,
WORD id, const char wclass[], const char caption[])
{
$1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL;
$ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary
$ ((ULONG&) pw) += 3;
$ ((ULONG&) pw) >>= 2;
$ ((ULONG&) pw) <<= 2;
$ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ tmpl->id = id;
$ if (HIWORD (wclass) == 0xFFFF)
{
$ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass));
$ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass));
}
else if (wclass)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ *pw++ = 0;
$ return pw;
}
//}
//=================================================================================================================
//=================================================================================================================
//{ Cleaning up the utility macros
// Очистка служебных макросов
//=================================================================================================================
#undef $
#undef $0
#undef $1
#undef $2
#undef $3
#undef $4
#undef $5
#undef $6
#undef $7
#undef $8
#undef $9
#undef $$
//}
//=================================================================================================================
//! @endcond
//=================================================================================================================
//{ Experimental Debugging macros
//! @name Экспериментальные отладочные макросы
//=================================================================================================================
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Misc
//! @brief Отладочная печать переменной во время вычисления выражения или участка кода
//! во время его выполнения.
//!
//! Сделай приятными твои <i>круглые сутки), 0;
14414 $
if (!bufsize) bufsize = 1024;
14416 $ DLGTEMPLATE* tmpl = (DLGTEMPLATE*) GlobalAlloc (GPTR, bufsize);
14417 $
if (!tmpl)
return TX_DEBUG_ERROR (
"GlobalAlloc(): Нет памяти для шаблона диалога ), 0;
$ const Layout* dlg = &layout[0];
$ const Layout def = { DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER, "MS Shell Dlg", 8 };
$ void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize,
(dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0,
dlg->x, dlg->y, dlg->sx, dlg->sy,
dlg->caption? dlg->caption : def.caption,
dlg->font? dlg->font : def.font,
dlg->fontsize? dlg->fontsize : def.fontsize, NULL);
$ WORD i = 0;
$ for (i = 1; layout[i].wndclass != END; ++i)
{
$ const Layout* item = &layout[i];
$ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((char*)ptr - (char*)tmpl),
item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy,
item->id, (const char*)(uintptr_t) item->wndclass, item->caption);
}
$ tmpl->cdit = (unsigned short) (i-1);
$ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC) DialogProc_, (LPARAM) this);
$ GlobalFree (tmpl);
$ return res;
}
//-----------------------------------------------------------------------------------------------------------------
int txDialog::dialogProc (HWND wnd, UINT msg, WPARAM wParam, LPARAM)
{
$1 switch (msg)
{
case WM_INITDIALOG: $ SetForegroundWindow (wnd);
$ break;
case WM_COMMAND: $ switch (LOWORD (wParam))
{
case IDOK:
case IDCANCEL: $ SetForegroundWindow (txWindow()? txWindow() : Win32::GetConsoleWindow());
$ EndDialog (wnd, (uintptr_t) this);
$ break;
default: $ break;
}
$ break;
default: $ break;
}
$ return FALSE;
}
//-----------------------------------------------------------------------------------------------------------------
intptr_t CALLBACK txDialog::DialogProc_ (HWND wnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
$1 static txDialog* this__ = NULL;
$ if (msg == WM_INITDIALOG) this__ = (txDialog*) lParam;
$ if (!this__) return FALSE;
$ return this__-> dialogProc (wnd, msg, wParam, lParam);
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Create (void* globalMem, size_t bufsize, DWORD style, DWORD exStyle,
WORD controls, short x, short y, short cx, short cy,
const char caption[], const char font[], WORD fontsize, const char menu[])
{
$1 if (_TX_ARGUMENT_FAILED (globalMem)) return NULL;
$ WORD* pw = (WORD*) globalMem;
$ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->cdit = controls;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ if (menu > (const char*) 0xFFFF)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (menu? menu : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
else
{
$ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0);
$ *pw++ = (WORD)(uintptr_t) menu;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (caption? caption : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ if (style & DS_SETFONT)
{
$ *pw++ = fontsize;
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, (font? font : ""), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) globalMem) : 0xFFFF));
}
$ return pw;
}
//-----------------------------------------------------------------------------------------------------------------
void* _tx_DLGTEMPLATE_Add (void* dlgTemplatePtr, size_t bufsize, DWORD style, DWORD exStyle,
short x, short y, short cx, short cy,
WORD id, const char wclass[], const char caption[])
{
$1 if (_TX_ARGUMENT_FAILED (dlgTemplatePtr)) return NULL;
$ WORD* pw = (LPWORD) dlgTemplatePtr; // Force align at word boundary
$ ((ULONG&) pw) += 3;
$ ((ULONG&) pw) >>= 2;
$ ((ULONG&) pw) <<= 2;
$ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++;
$ tmpl->style = style;
$ tmpl->dwExtendedStyle = exStyle;
$ tmpl->x = x;
$ tmpl->y = y;
$ tmpl->cx = cx;
$ tmpl->cy = cy;
$ tmpl->id = id;
$ if (HIWORD (wclass) == 0xFFFF)
{
$ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass));
$ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass));
}
else if (wclass)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, const_cast <char*> (wclass), -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ if (caption)
{
$ pw += MultiByteToWideChar (_TX_CODEPAGE, 0, caption, -1, (wchar_t*) pw,
(int) (bufsize? bufsize - ((char*) pw - (char*) dlgTemplatePtr) : 0xFFFF));
}
else
{
$ *pw++ = 0;
}
$ *pw++ = 0;
$ return pw;
}
//}
//=================================================================================================================
//=================================================================================================================
//{ Cleaning up the utility macros
// Очистка служебных макросов
//=================================================================================================================
#undef $
#undef $0
#undef $1
#undef $2
#undef $3
#undef $4
#undef $5
#undef $6
#undef $7
#undef $8
#undef $9
#undef $$
//}
//=================================================================================================================
//! @endcond
//=================================================================================================================
//{ Experimental Debugging macros
//! @name Экспериментальные отладочные макросы
//=================================================================================================================
//{----------------------------------------------------------------------------------------------------------------
//! @ingroup Misc
//! @brief Отладочная печать переменной во время вычисления выражения или участка кода
//! во время его выполнения.
//!
//! Сделай приятными твои <i>круглые сутки), 0;
14419 $
const Layout* dlg = &layout[0];
14420 $
const Layout def = {
DIALOG, NULL, 0, 0,0,0,0, WS_CAPTION | WS_SYSMENU | DS_MODALFRAME | DS_CENTER,
"MS Shell Dlg", 8 };
14422 $
void* ptr = _tx_DLGTEMPLATE_Create (tmpl, bufsize,
14423 (dlg->style? dlg->style : def.style) | DS_SETFONT, 0, 0,
14424 dlg->x, dlg->y, dlg->sx, dlg->sy,
14425 dlg->caption? dlg->caption : def.caption,
14426 dlg->font? dlg->font : def.font,
14427 dlg->fontsize? dlg->fontsize : def.fontsize, NULL);
14431 $
const Layout* item = &layout[i];
14433 $ ptr = _tx_DLGTEMPLATE_Add (ptr, bufsize - ((
char*)ptr - (
char*)tmpl),
14434 item->style | WS_VISIBLE, 0, item->x, item->y, item->sx, item->sy,
14435 item->id, (
const char*)(uintptr_t) item->wndclass, item->caption);
14438 $ tmpl->cdit = (
unsigned short) (i-1);
14440 $ intptr_t res = DialogBoxIndirectParam (NULL, tmpl, NULL, (DLGPROC)
DialogProc_, (LPARAM)
this);
14442 $ GlobalFree (tmpl);
14453 case WM_INITDIALOG: $ SetForegroundWindow (wnd);
14456 case WM_COMMAND: $
switch (LOWORD (wParam))
14459 case IDCANCEL: $ SetForegroundWindow (
txWindow()?
txWindow() : Win32::GetConsoleWindow());
14460 $ EndDialog (wnd, (uintptr_t)
this);
14476 $1
static txDialog* this__ = NULL;
14477 $
if (msg == WM_INITDIALOG) this__ = (
txDialog*) lParam;
14478 $
if (!this__)
return FALSE;
14480 $
return this__->
dialogProc (wnd, msg, wParam, lParam);
14485 void* _tx_DLGTEMPLATE_Create (
void* globalMem,
size_t bufsize, DWORD style, DWORD exStyle,
14486 WORD controls,
short x,
short y,
short cx,
short cy,
14487 const char caption[],
const char font[], WORD fontsize,
const char menu[])
14489 $1
if (_TX_ARGUMENT_FAILED (globalMem))
return NULL;
14491 $ WORD* pw = (WORD*) globalMem;
14493 $ DLGTEMPLATE* tmpl = ((DLGTEMPLATE*&) pw)++;
14495 $ tmpl->style = style;
14496 $ tmpl->dwExtendedStyle = exStyle;
14497 $ tmpl->cdit = controls;
14503 $
if (menu > (
const char*) 0xFFFF)
14505 $ pw += MultiByteToWideChar (
_TX_CODEPAGE, 0, (menu? menu :
""), -1, (
wchar_t*) pw,
14506 (
int) (bufsize? bufsize - ((
char*) pw - (
char*) globalMem) : 0xFFFF));
14510 $ *pw++ = (WORD)(uintptr_t) (menu? 0xFFFF : 0);
14511 $ *pw++ = (WORD)(uintptr_t) menu;
14516 $ pw += MultiByteToWideChar (
_TX_CODEPAGE, 0, (caption? caption :
""), -1, (
wchar_t*) pw,
14517 (
int) (bufsize? bufsize - ((
char*) pw - (
char*) globalMem) : 0xFFFF));
14520 $
if (style & DS_SETFONT)
14522 $ *pw++ = fontsize;
14523 $ pw += MultiByteToWideChar (
_TX_CODEPAGE, 0, (font? font :
""), -1, (
wchar_t*) pw,
14524 (
int) (bufsize? bufsize - ((
char*) pw - (
char*) globalMem) : 0xFFFF));
14532 void* _tx_DLGTEMPLATE_Add (
void* dlgTemplatePtr,
size_t bufsize, DWORD style, DWORD exStyle,
14533 short x,
short y,
short cx,
short cy,
14534 WORD
id,
const char wclass[],
const char caption[])
14536 $1
if (_TX_ARGUMENT_FAILED (dlgTemplatePtr))
return NULL;
14538 $ WORD* pw = (LPWORD) dlgTemplatePtr;
14539 $ ((ULONG&) pw) += 3;
14540 $ ((ULONG&) pw) >>= 2;
14541 $ ((ULONG&) pw) <<= 2;
14543 $ DLGITEMTEMPLATE* tmpl = ((DLGITEMTEMPLATE*&) pw)++;
14545 $ tmpl->style = style;
14546 $ tmpl->dwExtendedStyle = exStyle;
14553 $
if (HIWORD (wclass) == 0xFFFF)
14555 $ *pw++ = (WORD) (HIWORD ((uintptr_t) wclass));
14556 $ *pw++ = (WORD) (LOWORD ((uintptr_t) wclass));
14560 $ pw += MultiByteToWideChar (
_TX_CODEPAGE, 0,
const_cast <char*
> (wclass), -1, (
wchar_t*) pw,
14561 (
int) (bufsize? bufsize - ((
char*) pw - (
char*) dlgTemplatePtr) : 0xFFFF));
14570 $ pw += MultiByteToWideChar (
_TX_CODEPAGE, 0, caption, -1, (
wchar_t*) pw,
14571 (
int) (bufsize? bufsize - ((
char*) pw - (
char*) dlgTemplatePtr) : 0xFFFF));
14736 #ifndef __TX_DEBUG_MACROS
14737 #define __TX_DEBUG_MACROS ("Группа отладочных $-макросов")
14742 #define $H txSetConsoleAttr (FOREGROUND_BLACK | BACKGROUND_BLACK);
14743 #define $B txSetConsoleAttr (FOREGROUND_BLUE | BACKGROUND_BLACK);
14744 #define $G txSetConsoleAttr (FOREGROUND_GREEN | BACKGROUND_BLACK);
14745 #define $C txSetConsoleAttr (FOREGROUND_CYAN | BACKGROUND_BLACK);
14746 #define $R txSetConsoleAttr (FOREGROUND_RED | BACKGROUND_BLACK);
14747 #define $M txSetConsoleAttr (FOREGROUND_MAGENTA | BACKGROUND_BLACK);
14748 #define $Y txSetConsoleAttr (FOREGROUND_DARKYELLOW | BACKGROUND_BLACK);
14749 #define $d txSetConsoleAttr (FOREGROUND_LIGHTGRAY | BACKGROUND_BLACK);
14750 #define $D txSetConsoleAttr (FOREGROUND_DARKGRAY | BACKGROUND_BLACK);
14751 #define $b txSetConsoleAttr (FOREGROUND_LIGHTBLUE | BACKGROUND_BLACK);
14752 #define $g txSetConsoleAttr (FOREGROUND_LIGHTGREEN | BACKGROUND_BLACK);
14753 #define $c txSetConsoleAttr (FOREGROUND_LIGHTCYAN | BACKGROUND_BLACK);
14754 #define $r txSetConsoleAttr (FOREGROUND_LIGHTRED | BACKGROUND_BLACK);
14755 #define $m txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA | BACKGROUND_BLACK);
14756 #define $y txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_BLACK);
14757 #define $h txSetConsoleAttr (FOREGROUND_WHITE | BACKGROUND_BLACK);
14759 #define $i txSetConsoleAttr (FOREGROUND_LIGHTCYAN | BACKGROUND_BLUE);
14760 #define $I txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_BLUE);
14761 #define $o txSetConsoleAttr (FOREGROUND_LIGHTGREEN | BACKGROUND_GREEN);
14762 #define $O txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_GREEN);
14763 #define $e txSetConsoleAttr (FOREGROUND_WHITE | BACKGROUND_RED);
14764 #define $E txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_RED);
14765 #define $w txSetConsoleAttr (FOREGROUND_LIGHTMAGENTA | BACKGROUND_MAGENTA);
14766 #define $W txSetConsoleAttr (FOREGROUND_YELLOW | BACKGROUND_MAGENTA);
14767 #define $f txSetConsoleAttr (FOREGROUND_BLACK | BACKGROUND_LIGHTRED);
14768 #define $F txSetConsoleAttr (FOREGROUND_MAGENTA | BACKGROUND_LIGHTRED);
14769 #define $l txSetConsoleAttr (FOREGROUND_BLACK | BACKGROUND_DARKGRAY);
14770 #define $L txSetConsoleAttr (FOREGROUND_LIGHTGRAY | BACKGROUND_DARKGRAY);
14772 #define $T( cond ) txSetConsoleAttr ((cond)? FOREGROUND_LIGHTGREEN : FOREGROUND_LIGHTRED );
14774 #define $s _txSaveConsoleAttr TX_JOIN (__txSavedConsoleAttrs, __LINE__);
14806 #define $sT( cond ) $s $T (cond)
14808 #define $test(cond) { if (!!(cond)) { $o std::cerr << "[PASSED] " __TX_FILELINE__ ": " #cond; } \
14809 else { $e std::cerr << "[FAILED] " __TX_FILELINE__ ": " #cond; } $d; }
14811 #define $status(cond) $test (cond)
14813 #define $unittest( code, expected ) \
14815 const _tx_decltype (code) & _result = (code); \
14816 const _tx_decltype (expected) & _expected = (expected); \
14818 if (_result == _expected) \
14819 { $so std::cerr << "[PASSED] " __TX_FILELINE__ ": " #code; } \
14821 { $se std::cerr << "[FAILED] " __TX_FILELINE__ ": " #code " == (" << _result << "), should be (" << _expected << ")"; } \
14824 (_result == _expected); \
14831 #define $V( var, ...) ( _txDumpVar ((var), "[", __VA_ARGS__ "]\n") )
14832 #define $V_( var, ...) ( _txDumpVar ((var), "[", __VA_ARGS__ "] " ) )
14833 #define $V__(var, ...) ( _txDumpVar ((var), "[", __VA_ARGS__ "]" ) )
14835 #define $( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]\n") )
14836 #define $_( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "] " ) )
14837 #define $__( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]" ) )
14839 #define $x( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]\n", ::std::ios_base::showbase | ::std::ios_base::hex) )
14840 #define $x_( var, ...) ( _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "] ", ::std::ios_base::showbase | ::std::ios_base::hex) )
14842 #define $v( var, cond, ...) { { $st (cond); _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]" ); } $n; }
14843 #define $v_( var, cond, ...) { $st (cond); _txDumpVar ((var), "[" #var " = ", __VA_ARGS__ "]" ); }
14847 #define $V( var, ...) ( _txDumpVar ((var), "[", #__VA_ARGS__ "]\n") )
14848 #define $V_( var, ...) ( _txDumpVar ((var), "[", #__VA_ARGS__ "] " ) )
14849 #define $V__(var, ...) ( _txDumpVar ((var), "[", #__VA_ARGS__ "]" ) )
14851 #define $( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]\n") )
14852 #define $_( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "] " ) )
14853 #define $__( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]" ) )
14855 #define $x( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]\n", ::std::ios_base::showbase | ::std::ios_base::hex) )
14856 #define $x_( var, ...) ( _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "] ", ::std::ios_base::showbase | ::std::ios_base::hex) )
14858 #define $v( var, cond, ...) { { $st (cond); _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]" ); } $n; }
14859 #define $v_( var, cond, ...) { $st (cond); _txDumpVar ((var), "[" #var " = ", #__VA_ARGS__ "]" ); }
14863 #define $$ { txOutputDebugPrintf ("\f\n"); { $sC txOutputDebugPrintf ("\f" "[%s (%d) %s]", __FILE__, __LINE__, __TX_FUNCTION__); } txOutputDebugPrintf ("\f\n"); }
14864 #define $$_ { txOutputDebugPrintf ("\f\n"); { $sC txOutputDebugPrintf ("\f" "[" "(%d) %s]", __LINE__, __func__); } txOutputDebugPrintf ("\f\n"); }
14865 #define $meow(...) { txOutputDebugPrintf ("\f\n"); { $sc txOutputDebugPrintf ("\f" "[%s (%d) %s]", __FILE__, __LINE__, __func__); txOutputDebugPrintf ("\f " __VA_ARGS__); } txOutputDebugPrintf ("\f\n"); }
14866 #define $meow_ { txOutputDebugPrintf ("\f\n"); { $sc txOutputDebugPrintf ("\f" "[%s (%d) %s]", __FILE__, __LINE__, __func__); txOutputDebugPrintf ("\f"); } txOutputDebugPrintf ("\f\n"); }
14868 #define $$$( ... ) ( ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n", _txDumpVar ((__VA_ARGS__),"\n[" __TX_FILELINE__ ": " #__VA_ARGS__ ": ", ", DONE]\n\n") )
14869 #define $$$_( ... ) ( ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n", _txDumpVar ((__VA_ARGS__), "[" __TX_FILELINE__ ": " #__VA_ARGS__ ": ", ", DONE]\n\n") )
14871 #define $$$$( ... ) { ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n"; _txDumpVarSuffix ("\n[" __TX_FILELINE__ ": " #__VA_ARGS__ " DONE]\n\n"); { __VA_ARGS__; } }
14872 #define $$$$_( ... ) { ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n"; _txDumpVarSuffix ( "[" __TX_FILELINE__ ": " #__VA_ARGS__ " DONE]\n\n"); { __VA_ARGS__; } }
14873 #define $do( ... ) ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]\n"; __VA_ARGS__
14874 #define $DO( ... ) ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]...\n"; $p; __VA_ARGS__
14875 #define $Do( ... ) ::std::cerr << "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]...\n"; \
14876 txMessageBox ( "\n[" __TX_FILELINE__ ": " #__VA_ARGS__ "]...\n", __TX_FUNCTION__); __VA_ARGS__
14878 #define $n { ::std::cerr << "\n"; }
14879 #define $nn { ::std::cerr << "\n\n"; }
14880 #define $t { ::std::cerr << "\t"; }
14886 #if defined (_DEBUG)
14887 #define $debug if (1)
14888 #define $printf if (1)
14889 #define $PRINTF if (1)
14891 #define $debug if (0)
14892 #define $printf if (0)
14893 #define $PRINTF if (0)
14898 #define $$s __TX_FILELINE__
14899 #define $$b DebugBreak()
14900 #define $$p { if (txMessageBox (__TX_FILELINE__ "\n\n" "[Повтор] - продолжение программы,\n" "[Отмена] - остановка", \
14901 __TX_FUNCTION__, MB_ICONINFORMATION | MB_RETRYCANCEL) == IDCANCEL) ::exit (2); }
14902 #define $$P ( txMessageBox (__TX_FILELINE__, __TX_FUNCTION__, MB_ICONINFORMATION | MB_YESNOCANCEL) )
14903 #define $ppp { { $sy; fprintf (stderr, "[%s ""%s: ""Нажмите любую клавишу для продолжения]", __TX_FILELINE__, __TX_FUNCTION__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14904 #define $pp { { $sy; fprintf (stderr, "[%04d %s: ""Нажмите любую клавишу для продолжения]", __LINE__, __TX_FUNCTION__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14905 #define $p { { $sy; fprintf (stderr, "[%s ""%s(): Нажмите любую клавишу для продолжения]", __TX_FILELINE__, __func__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14906 #define $ppp_ { { $sy; fprintf (stderr, "[%s ""%s]", __TX_FILELINE__, __TX_FUNCTION__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14907 #define $pp_ { { $sy; fprintf (stderr, "[%04d %s]", __LINE__, __TX_FUNCTION__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14908 #define $p_ { { $sy; fprintf (stderr, "[%s ""%s()]", __TX_FILELINE__, __func__); } $P; { $sy; fprintf (stderr, "...\n"); } }
14909 #define $P ( ((int(*)()) (_getch)) () ) // Avoid "return value not used"
14913 struct _txSaveConsoleAttr
14924 struct _txDumpVarSuffix
14926 typedef _txDumpVarSuffix this_t;
14928 const char* suffix_;
14930 inline explicit _txDumpVarSuffix (
const char suffix[] =
"") : suffix_ (suffix) {
assert (suffix); }
14931 inline ~_txDumpVarSuffix() { ::std::cerr << suffix_; }
14933 _txDumpVarSuffix (
const this_t&) _tx_delete;
14934 this_t& operator = (
const this_t&) _tx_delete;
14939 #define ARGS__ const char* prefix, const char* suffix, std::ios_base::fmtflags flags, int deep
14940 #define ARGS_ const char* prefix, const char* suffix, std::ios_base::fmtflags flags = std::ios_base::fmtflags(), int deep = 0
14941 #define VALS_ prefix, suffix, flags, deep
14943 template <
typename T,
typename StreamT>
const T& _txDumpVal (
const T& value, StreamT& stream, ARGS_);
14947 template <
typename T>
inline const T& _txDumpVar (
const T& value, ARGS_) { _txDumpVal (value, std:: cerr, VALS_);
return value; }
14948 template <
typename T>
inline T& _txDumpVar ( T& value, ARGS_) { _txDumpVal (value, std:: cerr, VALS_);
return value; }
14950 template <
int N>
inline const char (&_txDumpVar (
const char (&value) [N], ARGS_)) [N] { _txDumpVal (value, std:: cerr, VALS_);
return value; }
14951 template <
int N>
inline char (&_txDumpVar (
char (&value) [N], ARGS_)) [N] { _txDumpVal (value, std:: cerr, VALS_);
return value; }
14953 template <
int N>
inline const wchar_t (&_txDumpVar (
const wchar_t (&value) [N], ARGS_)) [N] { _txDumpVal (value, std::wcerr, VALS_);
return value; }
14954 template <
int N>
inline wchar_t (&_txDumpVar (
wchar_t (&value) [N], ARGS_)) [N] { _txDumpVal (value, std::wcerr, VALS_);
return value; }
14956 inline const wchar_t& _txDumpVar (
const wchar_t& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_);
return value; }
14957 inline wchar_t& _txDumpVar (
wchar_t& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_);
return value; }
14959 inline const wchar_t*& _txDumpVar (
const wchar_t*& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_);
return value; }
14960 inline wchar_t*& _txDumpVar (
wchar_t*& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_);
return value; }
14962 inline const std::wstring& _txDumpVar (
const std::wstring& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_);
return value; }
14963 inline std::wstring& _txDumpVar ( std::wstring& value, ARGS_) { _txDumpVal (value, std::wcerr, VALS_);
return value; }
14967 template <
typename T,
typename StreamT>
inline void _txDumpVal (
const T& value, StreamT& stream) { stream << value; }
14968 template <
typename StreamT>
inline void _txDumpVal (
const char value, StreamT& stream) { stream <<
"'" << value <<
"'"; }
14969 template <
typename StreamT>
inline void _txDumpVal (
const wchar_t value, StreamT& stream) { stream << L
"'" << value << L
"'"; }
14970 template <
typename StreamT>
inline void _txDumpVal (
const char* value, StreamT& stream) {
if (value) stream <<
'"' << value <<
'"';
else stream <<
"(null)"; }
14971 template <
typename StreamT>
inline void _txDumpVal (
const wchar_t* value, StreamT& stream) {
if (value) stream << L
'"' << value << L
'"';
else stream << L
"(null)"; }
14972 template <
typename StreamT>
inline void _txDumpVal (
const std::string& value, StreamT& stream) {
if (value.length()) stream <<
'"' << value <<
'"';
else stream <<
"(empty)"; }
14973 template <
typename StreamT>
inline void _txDumpVal (
const std::wstring& value, StreamT& stream) {
if (value.length()) stream << L
'"' << value << L
'"';
else stream << L
"(empty)"; }
14977 template <
typename T,
typename StreamT>
14978 inline const T& _txDumpVal (
const T& value, StreamT& stream, ARGS__)
14980 if (_TX_ARGUMENT_FAILED (&value))
return value;
14981 if (_TX_ARGUMENT_FAILED (&stream))
return value;
14982 if (_TX_ARGUMENT_FAILED ( prefix))
return value;
14983 if (_TX_ARGUMENT_FAILED ( suffix))
return value;
14986 if (!deep) stream << prefix;
14988 std::ios_base::fmtflags old = stream.flags ((flags)? flags : stream.flags());
14990 if (!_txIsBadReadPtr (&value))
14992 _txDumpVal (value, stream);
14996 $sE; stream <<
"<НЕВЕРНЫЙ АДРЕС " << &value <<
">";
14999 stream.flags (old);
15001 if (!deep) stream << ((*suffix ==
']')?
"" :
": ") << suffix;
15008 template <
typename T,
int N>
15009 inline T (&_txDumpVar (T (&value) [N], ARGS_)) [N]
15011 if (_TX_ARGUMENT_FAILED (&value))
return value;
15012 if (_TX_ARGUMENT_FAILED ( prefix))
return value;
15013 if (_TX_ARGUMENT_FAILED ( suffix))
return value;
15015 std::ostream& stream = std::cerr;
15017 $sc;
if (!deep) std::cerr << prefix;
15018 $C; std::cerr << ((deep)?
" {" :
"{");
15020 if (!_txIsBadReadPtr (value))
15022 for (
int i = 0; ; i++)
15024 { $sC; stream <<
"[" << i <<
"]="; }
15026 _txDumpVar (value[i], prefix, suffix, flags, deep+1);
15028 if (i >= N-1)
break;
15035 $sE; stream <<
"<НЕВЕРНЫЙ АДРЕС " << &value <<
">";
15038 $C; std::cerr <<
"}";
15039 $c;
if (!deep) std::cerr << ((*suffix ==
']')?
"" :
": ") << suffix;
15050 inline std::ostream& operator << (std::ostream& stream,
const POINT& point)
15052 if (_TX_ARGUMENT_FAILED (&stream))
return stream;
15053 if (_TX_ARGUMENT_FAILED (&point))
return stream;
15055 if (&point) stream <<
"{ x: " << point.x <<
", y: " << point.y <<
" }";
15056 else stream <<
"(null)";
15061 inline std::ostream& operator << (std::ostream& stream,
const SIZE& size)
15063 if (_TX_ARGUMENT_FAILED (&stream))
return stream;
15064 if (_TX_ARGUMENT_FAILED (&size))
return stream;
15066 if (&size) stream <<
"{ cx: " << size.cx <<
", cy: " << size.cy <<
" }";
15067 else stream <<
"(null)";
15072 inline std::ostream& operator << (std::ostream& stream,
const RECT& rect)
15074 if (_TX_ARGUMENT_FAILED (&stream))
return stream;
15075 if (_TX_ARGUMENT_FAILED (&rect))
return stream;
15077 if (&rect) stream <<
"{ left: " << rect.left <<
", top: " << rect.top <<
15078 ", right: " << rect.right <<
", bottom: " << rect.bottom <<
" }";
15080 else stream <<
"(null)";
15100 #ifndef FOR_DOXYGEN_ONLY
15102 #if defined (_MSC_VER)
15103 #undef _txLocCurSet
15104 #define _txLocCurSet() __txLocCurSet (__FILE__, __LINE__, NULL)
15107 #define txAlphaBlend(...) ( _txLocCurSet(), txAlphaBlend (__VA_ARGS__) )
15108 #define txArc(...) ( _txLocCurSet(), txArc (__VA_ARGS__) )
15109 #define txBegin(...) ( _txLocCurSet(), txBegin (__VA_ARGS__) )
15110 #define txBitBlt(...) ( _txLocCurSet(), txBitBlt (__VA_ARGS__) )
15111 #define txChord(...) ( _txLocCurSet(), txChord (__VA_ARGS__) )
15112 #define txCircle(...) ( _txLocCurSet(), txCircle (__VA_ARGS__) )
15113 #define txClear(...) ( _txLocCurSet(), txClear (__VA_ARGS__) )
15114 #define txClearConsole(...) ( _txLocCurSet(), txClearConsole (__VA_ARGS__) )
15115 #define txColor(...) ( _txLocCurSet(), txColor (__VA_ARGS__) )
15116 #define txCreateCompatibleDC(...) ( _txLocCurSet(), txCreateCompatibleDC (__VA_ARGS__) )
15117 #define txCreateDIBSection(...) ( _txLocCurSet(), txCreateDIBSection (__VA_ARGS__) )
15118 #define txCreateExtraWindow(...) ( _txLocCurSet(), txCreateExtraWindow (__VA_ARGS__) )
15119 #define txCreateExtraWindow(...) ( _txLocCurSet(), txCreateExtraWindow (__VA_ARGS__) )
15120 #define txCreateWindow(...) ( _txLocCurSet(), txCreateWindow (__VA_ARGS__) )
15121 #define txDC(...) ( _txLocCurSet(), txDC (__VA_ARGS__) )
15122 #define txDeleteDC(...) ( _txLocCurSet(), txDeleteDC (__VA_ARGS__) )
15123 #define txDemangle(...) ( _txLocCurSet(), txDemangle (__VA_ARGS__) )
15124 #define txDestroyWindow(...) ( _txLocCurSet(), txDestroyWindow (__VA_ARGS__) )
15125 #define txDisableAutoPause(...) ( _txLocCurSet(), txDisableAutoPause (__VA_ARGS__) )
15126 #define txDrawText(...) ( _txLocCurSet(), txDrawText (__VA_ARGS__) )
15127 #define txEllipse(...) ( _txLocCurSet(), txEllipse (__VA_ARGS__) )
15128 #define txEnd(...) ( _txLocCurSet(), txEnd (__VA_ARGS__) )
15129 #define txExtractColor(...) ( _txLocCurSet(), txExtractColor (__VA_ARGS__) )
15130 #define txFillColor(...) ( _txLocCurSet(), txFillColor (__VA_ARGS__) )
15131 #define txFloodFill(...) ( _txLocCurSet(), txFloodFill (__VA_ARGS__) )
15132 #define txFontExist(...) ( _txLocCurSet(), txFontExist (__VA_ARGS__) )
15133 #define txFormat(...) ( _txLocCurSet(), txFormat (__VA_ARGS__) )
15134 #define txGetAsyncKeyState(...) ( _txLocCurSet(), txGetAsyncKeyState (__VA_ARGS__) )
15135 #define txGetColor(...) ( _txLocCurSet(), txGetColor (__VA_ARGS__) )
15136 #define txGetConsoleAttr(...) ( _txLocCurSet(), txGetConsoleAttr (__VA_ARGS__) )
15137 #define txGetConsoleCursorPos(...) ( _txLocCurSet(), txGetConsoleCursorPos (__VA_ARGS__) )
15138 #define txGetConsoleExtent(...) ( _txLocCurSet(), txGetConsoleExtent (__VA_ARGS__) )
15139 #define txGetConsoleFontSize(...) ( _txLocCurSet(), txGetConsoleFontSize (__VA_ARGS__) )
15140 #define txGetExtent(...) ( _txLocCurSet(), txGetExtent (__VA_ARGS__) )
15141 #define txGetExtentX(...) ( _txLocCurSet(), txGetExtentX (__VA_ARGS__) )
15142 #define txGetExtentY(...) ( _txLocCurSet(), txGetExtentY (__VA_ARGS__) )
15143 #define txGetFillColor(...) ( _txLocCurSet(), txGetFillColor (__VA_ARGS__) )
15144 #define txGetFPS(...) ( _txLocCurSet(), txGetFPS (__VA_ARGS__) )
15145 #define txGetModuleFileName(...) ( _txLocCurSet(), txGetModuleFileName (__VA_ARGS__) )
15146 #define txGetPixel(...) ( _txLocCurSet(), txGetPixel (__VA_ARGS__) )
15147 #define txGetTextExtent(...) ( _txLocCurSet(), txGetTextExtent (__VA_ARGS__) )
15148 #define txGetTextExtentX(...) ( _txLocCurSet(), txGetTextExtentX (__VA_ARGS__) )
15149 #define txGetTextExtentY(...) ( _txLocCurSet(), txGetTextExtentY (__VA_ARGS__) )
15150 #define txHSL2RGB(...) ( _txLocCurSet(), txHSL2RGB (__VA_ARGS__) )
15151 #define txInputBox(...) ( _txLocCurSet(), txInputBox (__VA_ARGS__) )
15152 #define txLine(...) ( _txLocCurSet(), txLine (__VA_ARGS__) )
15153 #define txLoadImage(...) ( _txLocCurSet(), txLoadImage (__VA_ARGS__) )
15154 #define txLock(...) ( _txLocCurSet(), txLock (__VA_ARGS__) )
15155 #define txMessageBox(...) ( _txLocCurSet(), txMessageBox (__VA_ARGS__) )
15156 #define txMouseButtons(...) ( _txLocCurSet(), txMouseButtons (__VA_ARGS__) )
15157 #define txMousePos(...) ( _txLocCurSet(), txMousePos (__VA_ARGS__) )
15158 #define txMouseX(...) ( _txLocCurSet(), txMouseX (__VA_ARGS__) )
15159 #define txMouseY(...) ( _txLocCurSet(), txMouseY (__VA_ARGS__) )
15160 #define txNotifyIcon(...) ( _txLocCurSet(), txNotifyIcon (__VA_ARGS__) )
15161 #define txOK(...) ( _txLocCurSet(), txOK (__VA_ARGS__) )
15162 #define txOutputDebugPrintf(...) ( _txLocCurSet(), txOutputDebugPrintf (__VA_ARGS__) )
15163 #define txPie(...) ( _txLocCurSet(), txPie (__VA_ARGS__) )
15164 #define txPixel(...) ( _txLocCurSet(), txPixel (__VA_ARGS__) )
15165 #define txPlaySound(...) ( _txLocCurSet(), txPlaySound (__VA_ARGS__) )
15166 #define txPlayVideo(...) ( _txLocCurSet(), txPlayVideo (__VA_ARGS__) )
15167 #define txPolygon(...) ( _txLocCurSet(), txPolygon (__VA_ARGS__) )
15168 #define txPrintf(...) ( _txLocCurSet(), txPrintf (__VA_ARGS__) )
15169 #define txQueryPerformance(...) ( _txLocCurSet(), txQueryPerformance (__VA_ARGS__) )
15170 #define txRectangle(...) ( _txLocCurSet(), txRectangle (__VA_ARGS__) )
15171 #define txRedrawWindow(...) ( _txLocCurSet(), txRedrawWindow (__VA_ARGS__) )
15172 #define txRegisterClass(...) ( _txLocCurSet(), txRegisterClass (__VA_ARGS__) )
15173 #define txRegisterClass(...) ( _txLocCurSet(), txRegisterClass (__VA_ARGS__) )
15174 #define txRegQuery(...) ( _txLocCurSet(), txRegQuery (__VA_ARGS__) )
15175 #define txReopenStdio(...) ( _txLocCurSet(), txReopenStdio (__VA_ARGS__) )
15176 #define txReopenStdio(...) ( _txLocCurSet(), txReopenStdio (__VA_ARGS__) )
15177 #define txRGB2HSL(...) ( _txLocCurSet(), txRGB2HSL (__VA_ARGS__) )
15178 #define txSaveImage(...) ( _txLocCurSet(), txSaveImage (__VA_ARGS__) )
15179 #define txSelectFont(...) ( _txLocCurSet(), txSelectFont (__VA_ARGS__) )
15180 #define txSelectObject(...) ( _txLocCurSet(), txSelectObject (__VA_ARGS__) )
15181 #define txSetColor(...) ( _txLocCurSet(), txSetColor (__VA_ARGS__) )
15182 #define txSetConsoleAttr(...) ( _txLocCurSet(), txSetConsoleAttr (__VA_ARGS__) )
15183 #define txSetConsoleCursorPos(...) ( _txLocCurSet(), txSetConsoleCursorPos (__VA_ARGS__) )
15184 #define txSetDefaults(...) ( _txLocCurSet(), txSetDefaults (__VA_ARGS__) )
15185 #define txSetFillColor(...) ( _txLocCurSet(), txSetFillColor (__VA_ARGS__) )
15186 #define txSetLocale(...) ( _txLocCurSet(), txSetLocale (__VA_ARGS__) )
15187 #define txSetPixel(...) ( _txLocCurSet(), txSetPixel (__VA_ARGS__) )
15188 #define txSetTextAlign(...) ( _txLocCurSet(), txSetTextAlign (__VA_ARGS__) )
15189 #define txSetWindowsHook(...) ( _txLocCurSet(), txSetWindowsHook (__VA_ARGS__) )
15190 #define txSleep(...) ( _txLocCurSet(), txSleep (__VA_ARGS__) )
15191 #define txSpeak(...) ( _txLocCurSet(), txSpeak (__VA_ARGS__) )
15192 #define txTaskKill(...) ( _txLocCurSet(), txTaskKill (__VA_ARGS__) )
15193 #define txTextCursor(...) ( _txLocCurSet(), txTextCursor (__VA_ARGS__) )
15194 #define txTextOut(...) ( _txLocCurSet(), txTextOut (__VA_ARGS__) )
15195 #define txTransparentBlt(...) ( _txLocCurSet(), txTransparentBlt (__VA_ARGS__) )
15196 #define txTriangle(...) ( _txLocCurSet(), txTriangle (__VA_ARGS__) )
15197 #define txUnlock(...) ( _txLocCurSet(), txUnlock (__VA_ARGS__) )
15198 #define txUpdateWindow(...) ( _txLocCurSet(), txUpdateWindow (__VA_ARGS__) )
15199 #define txUseAlpha(...) ( _txLocCurSet(), txUseAlpha (__VA_ARGS__) )
15200 #define txVersion(...) ( _txLocCurSet(), txVersion (__VA_ARGS__) )
15201 #define txVersionNumber(...) ( _txLocCurSet(), txVersionNumber (__VA_ARGS__) )
15202 #define txWindow(...) ( _txLocCurSet(), txWindow (__VA_ARGS__) )
15203 #define tx_fpreset(...) ( _txLocCurSet(), tx_fpreset (__VA_ARGS__) )
15204 #define tx_glGetError(...) ( _txLocCurSet(), tx_glGetError (__VA_ARGS__) )
15205 #define _txDump(...) ( _txLocCurSet(), _txDump (__VA_ARGS__) )
15206 #define _txStackBackTrace(...) ( _txLocCurSet(), _txStackBackTrace (__VA_ARGS__) )
15227 using namespace TX;
15232 using ::std::string;
15234 using ::std::wcout;
15235 using ::std::wcerr;
15236 using ::std::wstring;
15247 #if defined (_GCC_VER)
15249 #pragma GCC optimize "strict-aliasing"
15251 #pragma GCC pop_options
15252 #pragma GCC diagnostic pop
15256 #if defined (_CLANG_VER)
15258 #pragma clang diagnostic pop
15264 #if defined (_MSC_VER)
15266 #pragma warning (pop) // Restoring maximum level
15270 #if defined (__INTEL_COMPILER)
15272 #pragma warning (default: 174) // Remark: expression has no effect
15273 #pragma warning (default: 304) // Remark: access control not specified ("public" by default)
15274 #pragma warning (default: 444) // Remark: destructor for base class "..." is not virtual
15275 #pragma warning (default: 522) // Remark: function redeclared "inline" after being called
15276 #pragma warning (default: 1684) // Conversion from pointer to same-sized integral type (potential portability problem)
15278 #pragma warning (disable: 981) // Remark: operands are evaluated in unspecified order
15286 #endif // __TXLIB_H_INCLUDED